优化页面布局
This commit is contained in:
parent
8887da8ba9
commit
dd9c117df7
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="message-role">Agent</div>
|
||||
<div class="message-text">
|
||||
<div v-if="message.content" v-html="markdownToHtml(props.message.content)"></div>
|
||||
</div>
|
||||
<MessageMeta :message="props.message" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps } from 'vue';
|
||||
import { markdownToHtml } from '../markdown';
|
||||
|
||||
import MessageMeta from './message-meta.vue';
|
||||
|
||||
const props = defineProps({
|
||||
message: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
4
renderer/src/components/main-panel/chat/message/index.ts
Normal file
4
renderer/src/components/main-panel/chat/message/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import Assistant from "./assistant.vue";
|
||||
import Toolcall from "./toolcall.vue";
|
||||
import User from "./user.vue";
|
||||
export { Assistant, Toolcall, User };
|
@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<div class="message-meta" @mouseenter="showTime = true" @mouseleave="showTime = false">
|
||||
<span v-if="usageStatistic" class="message-usage">
|
||||
<span>
|
||||
{{ t('input-token') }} {{ usageStatistic.input }}
|
||||
</span>
|
||||
|
||||
<span>
|
||||
{{ t('output-token') }} {{ usageStatistic.output }}
|
||||
</span>
|
||||
|
||||
<span>
|
||||
{{ t('total') }} {{ usageStatistic.total }}
|
||||
</span>
|
||||
|
||||
<span>
|
||||
{{ t('cache-hit-ratio') }} {{ usageStatistic.cacheHitRatio }}%
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span v-else class="message-usage">
|
||||
<span>{{ t('server-not-support-statistic') }}</span>
|
||||
</span>
|
||||
|
||||
<span v-show="showTime" class="message-time">
|
||||
{{ props.message.extraInfo.serverName }} {{ t('answer-at') }}
|
||||
{{ new Date(message.extraInfo.created).toLocaleString() }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineComponent, defineProps, ref, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { makeUsageStatistic } from '../usage';
|
||||
|
||||
defineComponent({ name: 'message-meta' });
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
message: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const usageStatistic = computed(() => {
|
||||
return makeUsageStatistic(props.message.extraInfo);
|
||||
});
|
||||
|
||||
console.log(props.message);
|
||||
console.log(usageStatistic);
|
||||
|
||||
const showTime = ref(false);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.message-meta {
|
||||
margin-top: 8px;
|
||||
font-size: 0.8em;
|
||||
color: var(--el-text-color-secondary);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.message-time {
|
||||
opacity: 0.7;
|
||||
padding: 2px 6px 2px 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.message-usage {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.message-usage > span {
|
||||
background-color: var(--el-fill-color-light);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
</style>
|
156
renderer/src/components/main-panel/chat/message/toolcall.vue
Normal file
156
renderer/src/components/main-panel/chat/message/toolcall.vue
Normal file
@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<div class="message-role">
|
||||
Agent
|
||||
<span class="message-reminder" v-if="!props.message.toolResult">
|
||||
正在使用工具
|
||||
<span class="tool-loading iconfont icon-double-loading">
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="message-text tool_calls">
|
||||
<div v-if="props.message.content" v-html="markdownToHtml(props.message.content)"></div>
|
||||
|
||||
<div class="tool-calls">
|
||||
<div v-for="(call, index) in props.message.tool_calls" :key="index" class="tool-call-item">
|
||||
<div class="tool-call-header">
|
||||
<span class="tool-type">{{ 'tool' }}</span>
|
||||
<span class="tool-name">{{ call.function.name }}</span>
|
||||
<el-button size="small" @click="createTest(call)">
|
||||
<span class="iconfont icon-send"></span>
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="tool-arguments">
|
||||
<div class="inner">
|
||||
<div v-html="jsonResultToHtml(call.function.arguments)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 工具调用结果 -->
|
||||
<div v-if="props.message.toolResult">
|
||||
<div class="tool-call-header">
|
||||
<span class="tool-name">{{ "响应" }}</span>
|
||||
<span style="width: 200px;" class="tools-dialog-container">
|
||||
<el-switch v-model="props.message.showJson!.value" inline-prompt active-text="JSON" inactive-text="Text"
|
||||
style="margin-left: 10px; width: 200px;"
|
||||
:inactive-action-style="'backgroundColor: var(--sidebar)'" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="tool-result" v-if="isValidJSON(props.message.toolResult)">
|
||||
<div v-if="props.message.showJson!.value" class="tool-result-content">
|
||||
<div class="inner">
|
||||
<div v-html="jsonResultToHtml(props.message.toolResult)"></div>
|
||||
</div>
|
||||
</div>
|
||||
<span v-else>
|
||||
<div v-for="(item, index) in JSON.parse(props.message.toolResult)" :key="index">
|
||||
<el-scrollbar width="100%">
|
||||
<div v-if="item.type === 'text'" class="tool-text">{{ item.text }}</div>
|
||||
<div v-else class="tool-other">{{ JSON.stringify(item) }}</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MessageMeta :message="message" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps } from 'vue';
|
||||
|
||||
import MessageMeta from './message-meta.vue';
|
||||
import { markdownToHtml } from '../markdown';
|
||||
import { createTest } from '@/views/setting/llm';
|
||||
|
||||
const props = defineProps({
|
||||
message: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const jsonResultToHtml = (jsonString: string) => {
|
||||
const formattedJson = JSON.stringify(JSON.parse(jsonString), null, 2);
|
||||
const html = markdownToHtml('```json\n' + formattedJson + '\n```');
|
||||
return html;
|
||||
};
|
||||
|
||||
const isValidJSON = (str: string) => {
|
||||
try {
|
||||
JSON.parse(str);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.tool-calls {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.tool-call-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tool-call-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.tool-name {
|
||||
font-weight: bold;
|
||||
color: var(--el-color-primary);
|
||||
margin-right: 8px;
|
||||
margin-bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.tool-type {
|
||||
font-size: 0.8em;
|
||||
color: var(--el-text-color-secondary);
|
||||
background-color: var(--el-fill-color-light);
|
||||
padding: 2px 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 4px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.tool-arguments {
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
background-color: var(--el-fill-color-light);
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.tool-result {
|
||||
padding: 8px;
|
||||
background-color: var(--el-fill-color-light);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.tool-text {
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.tool-other {
|
||||
font-family: monospace;
|
||||
font-size: 0.9em;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
22
renderer/src/components/main-panel/chat/message/user.vue
Normal file
22
renderer/src/components/main-panel/chat/message/user.vue
Normal file
@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div class="message-role"></div>
|
||||
<div class="message-text">
|
||||
<span>{{ props.message.content }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import { defineProps } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
message: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
27
service/src/controller/env-var.ts
Normal file
27
service/src/controller/env-var.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { PostMessageble } from "../adapter";
|
||||
import { MCPClient } from "./connect";
|
||||
|
||||
|
||||
export async function lookupEnvVarHandler(client: MCPClient | undefined, data: any, webview: PostMessageble) {
|
||||
try {
|
||||
const { keys } = data;
|
||||
|
||||
const values = keys.map((key: string) => process.env[key] || '');
|
||||
|
||||
webview.postMessage({
|
||||
command: 'lookup-env-var',
|
||||
data: {
|
||||
code: 200,
|
||||
msg: values
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
webview.postMessage({
|
||||
command: 'lookup-env-var',
|
||||
data: {
|
||||
code: 500,
|
||||
msg: `Failed to lookup env vars: ${(error as Error).message}`
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user