优化上下文
This commit is contained in:
@@ -27,14 +27,15 @@ type ConfigsFile_ struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ConfigsAIChatOpenAI_ struct {
|
type ConfigsAIChatOpenAI_ struct {
|
||||||
Name string `mapstructure:"name"`
|
Name string `mapstructure:"name"`
|
||||||
Active bool `mapstructure:"active"`
|
Active bool `mapstructure:"active"`
|
||||||
ApiKey string `mapstructure:"apiKey"`
|
ApiKey string `mapstructure:"apiKey"`
|
||||||
BaseUrl string `mapstructure:"baseUrl"`
|
BaseUrl string `mapstructure:"baseUrl"`
|
||||||
Model string `mapstructure:"model"`
|
Model string `mapstructure:"model"`
|
||||||
Timeout int `mapstructure:"timeout"`
|
Timeout int `mapstructure:"timeout"`
|
||||||
MaxTokens int `mapstructure:"maxTokens"`
|
MaxTokens int `mapstructure:"maxTokens"`
|
||||||
SystemPrompt string `mapstructure:"systemPrompt"`
|
ContextWindowTokens int `mapstructure:"contextWindowTokens"`
|
||||||
|
SystemPrompt string `mapstructure:"systemPrompt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigsAIChatTool_ struct {
|
type ConfigsAIChatTool_ struct {
|
||||||
|
|||||||
@@ -180,12 +180,13 @@ func handleOpenAIProfiles(ctx *gin.Context) {
|
|||||||
active = profile.Name
|
active = profile.Name
|
||||||
}
|
}
|
||||||
profiles = append(profiles, map[string]interface{}{
|
profiles = append(profiles, map[string]interface{}{
|
||||||
"name": profile.Name,
|
"name": profile.Name,
|
||||||
"active": profile.Active,
|
"active": profile.Active,
|
||||||
"baseUrl": profile.BaseUrl,
|
"baseUrl": profile.BaseUrl,
|
||||||
"model": profile.Model,
|
"model": profile.Model,
|
||||||
"timeout": profile.Timeout,
|
"timeout": profile.Timeout,
|
||||||
"maxTokens": profile.MaxTokens,
|
"maxTokens": profile.MaxTokens,
|
||||||
|
"contextWindowTokens": profile.ContextWindowTokens,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ReturnJson(ctx, "apiOK", gin.H{
|
ReturnJson(ctx, "apiOK", gin.H{
|
||||||
@@ -317,6 +318,17 @@ func handleChat(ctx *gin.Context) {
|
|||||||
apiReq.Messages = append([]openaiMessage{{Role: "system", Content: profile.SystemPrompt}}, apiReq.Messages...)
|
apiReq.Messages = append([]openaiMessage{{Role: "system", Content: profile.SystemPrompt}}, apiReq.Messages...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trimmedMessages, trimStats := trimOpenAIMessagesToContextWindow(apiReq.Messages, profile.ContextWindowTokens)
|
||||||
|
apiReq.Messages = trimmedMessages
|
||||||
|
if trimStats.RemovedMessages > 0 {
|
||||||
|
emitTrace("model", "context_window", "success", "上下文窗口已裁剪旧消息", map[string]interface{}{
|
||||||
|
"limit": trimStats.Limit,
|
||||||
|
"before_tokens": trimStats.BeforeTokens,
|
||||||
|
"after_tokens": trimStats.AfterTokens,
|
||||||
|
"removed_messages": trimStats.RemovedMessages,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
modelPromptTokens := estimateOpenAIMessagesTokens(apiReq.Messages)
|
modelPromptTokens := estimateOpenAIMessagesTokens(apiReq.Messages)
|
||||||
completionTokens := 0
|
completionTokens := 0
|
||||||
modelUsageReceived := false
|
modelUsageReceived := false
|
||||||
@@ -603,6 +615,74 @@ func normalizeImageDataURI(raw string) (string, error) {
|
|||||||
return "data:" + mimeType + ";base64," + payload, nil
|
return "data:" + mimeType + ";base64," + payload, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type contextWindowTrimStats struct {
|
||||||
|
Enabled bool
|
||||||
|
Limit int
|
||||||
|
BeforeTokens int
|
||||||
|
AfterTokens int
|
||||||
|
RemovedMessages int
|
||||||
|
}
|
||||||
|
|
||||||
|
func trimOpenAIMessagesToContextWindow(messages []openaiMessage, maxTokens int) ([]openaiMessage, contextWindowTrimStats) {
|
||||||
|
stats := contextWindowTrimStats{Enabled: maxTokens > 0, Limit: maxTokens}
|
||||||
|
if maxTokens <= 0 || len(messages) == 0 {
|
||||||
|
stats.BeforeTokens = estimateOpenAIMessagesTokens(messages)
|
||||||
|
stats.AfterTokens = stats.BeforeTokens
|
||||||
|
return messages, stats
|
||||||
|
}
|
||||||
|
|
||||||
|
result := append([]openaiMessage(nil), messages...)
|
||||||
|
stats.BeforeTokens = estimateOpenAIMessagesTokens(result)
|
||||||
|
stats.AfterTokens = stats.BeforeTokens
|
||||||
|
if stats.BeforeTokens <= maxTokens {
|
||||||
|
return result, stats
|
||||||
|
}
|
||||||
|
|
||||||
|
for stats.AfterTokens > maxTokens {
|
||||||
|
startIndex := 0
|
||||||
|
if len(result) > 0 && result[0].Role == "system" {
|
||||||
|
startIndex = 1
|
||||||
|
}
|
||||||
|
latestUserIndex := latestUserMessageIndex(result)
|
||||||
|
removeIndex := -1
|
||||||
|
for i := startIndex; i < len(result); i++ {
|
||||||
|
if i == latestUserIndex {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if result[i].Role == "system" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
removeIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if removeIndex == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCount := 1
|
||||||
|
if result[removeIndex].Role == "user" {
|
||||||
|
nextIndex := removeIndex + 1
|
||||||
|
if nextIndex < len(result) && nextIndex != latestUserIndex && result[nextIndex].Role == "assistant" {
|
||||||
|
removeCount = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = append(result[:removeIndex], result[removeIndex+removeCount:]...)
|
||||||
|
stats.RemovedMessages += removeCount
|
||||||
|
stats.AfterTokens = estimateOpenAIMessagesTokens(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, stats
|
||||||
|
}
|
||||||
|
|
||||||
|
func latestUserMessageIndex(messages []openaiMessage) int {
|
||||||
|
for i := len(messages) - 1; i >= 0; i-- {
|
||||||
|
if messages[i].Role == "user" {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
type tokenUsageTracker struct {
|
type tokenUsageTracker struct {
|
||||||
promptTokens int
|
promptTokens int
|
||||||
completionTokens int
|
completionTokens int
|
||||||
|
|||||||
@@ -18,18 +18,19 @@ type TabAIChatSetting struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TabAIChatOpenAIProfile struct {
|
type TabAIChatOpenAIProfile struct {
|
||||||
ID uint `gorm:"primaryKey;autoIncrement"`
|
ID uint `gorm:"primaryKey;autoIncrement"`
|
||||||
Name string `gorm:"size:100;not null;uniqueIndex"`
|
Name string `gorm:"size:100;not null;uniqueIndex"`
|
||||||
Active bool `gorm:"default:false;index"`
|
Active bool `gorm:"default:false;index"`
|
||||||
ApiKey string `gorm:"type:text"`
|
ApiKey string `gorm:"type:text"`
|
||||||
BaseUrl string `gorm:"size:500"`
|
BaseUrl string `gorm:"size:500"`
|
||||||
Model string `gorm:"size:200"`
|
Model string `gorm:"size:200"`
|
||||||
Timeout int `gorm:"default:120"`
|
Timeout int `gorm:"default:120"`
|
||||||
MaxTokens int `gorm:"default:4096"`
|
MaxTokens int `gorm:"default:4096"`
|
||||||
SystemPrompt string `gorm:"type:text"`
|
ContextWindowTokens int `gorm:"default:0"`
|
||||||
SortOrder int `gorm:"default:0;index"`
|
SystemPrompt string `gorm:"type:text"`
|
||||||
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime"`
|
SortOrder int `gorm:"default:0;index"`
|
||||||
UpdatedAt *time.Time `gorm:"type:datetime;autoUpdateTime"`
|
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime"`
|
||||||
|
UpdatedAt *time.Time `gorm:"type:datetime;autoUpdateTime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TabAIChatToolRouter struct {
|
type TabAIChatToolRouter struct {
|
||||||
@@ -120,12 +121,13 @@ func seedAIChatConfigFromYAMLIfEmpty() error {
|
|||||||
profiles := cfg.OpenAI
|
profiles := cfg.OpenAI
|
||||||
if len(profiles) == 0 {
|
if len(profiles) == 0 {
|
||||||
profiles = []models.ConfigsAIChatOpenAI_{{
|
profiles = []models.ConfigsAIChatOpenAI_{{
|
||||||
Name: "default",
|
Name: "default",
|
||||||
Active: true,
|
Active: true,
|
||||||
BaseUrl: "https://ark.cn-beijing.volces.com/api/v3",
|
BaseUrl: "https://ark.cn-beijing.volces.com/api/v3",
|
||||||
Timeout: 120,
|
Timeout: 120,
|
||||||
MaxTokens: 4096,
|
MaxTokens: 4096,
|
||||||
SystemPrompt: "你是一个有帮助的 AI 助手。",
|
ContextWindowTokens: 0,
|
||||||
|
SystemPrompt: "你是一个有帮助的 AI 助手。",
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
for i, profile := range profiles {
|
for i, profile := range profiles {
|
||||||
@@ -139,15 +141,16 @@ func seedAIChatConfigFromYAMLIfEmpty() error {
|
|||||||
profile.MaxTokens = 4096
|
profile.MaxTokens = 4096
|
||||||
}
|
}
|
||||||
if err := tx.Create(&TabAIChatOpenAIProfile{
|
if err := tx.Create(&TabAIChatOpenAIProfile{
|
||||||
Name: profile.Name,
|
Name: profile.Name,
|
||||||
Active: profile.Active,
|
Active: profile.Active,
|
||||||
ApiKey: profile.ApiKey,
|
ApiKey: profile.ApiKey,
|
||||||
BaseUrl: profile.BaseUrl,
|
BaseUrl: profile.BaseUrl,
|
||||||
Model: profile.Model,
|
Model: profile.Model,
|
||||||
Timeout: profile.Timeout,
|
Timeout: profile.Timeout,
|
||||||
MaxTokens: profile.MaxTokens,
|
MaxTokens: profile.MaxTokens,
|
||||||
SystemPrompt: profile.SystemPrompt,
|
ContextWindowTokens: nonNegativeInt(profile.ContextWindowTokens),
|
||||||
SortOrder: i,
|
SystemPrompt: profile.SystemPrompt,
|
||||||
|
SortOrder: i,
|
||||||
}).Error; err != nil {
|
}).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -232,14 +235,15 @@ func RefreshAIChatConfigCache() error {
|
|||||||
|
|
||||||
for _, profile := range profiles {
|
for _, profile := range profiles {
|
||||||
cfg.OpenAI = append(cfg.OpenAI, models.ConfigsAIChatOpenAI_{
|
cfg.OpenAI = append(cfg.OpenAI, models.ConfigsAIChatOpenAI_{
|
||||||
Name: profile.Name,
|
Name: profile.Name,
|
||||||
Active: profile.Active,
|
Active: profile.Active,
|
||||||
ApiKey: profile.ApiKey,
|
ApiKey: profile.ApiKey,
|
||||||
BaseUrl: profile.BaseUrl,
|
BaseUrl: profile.BaseUrl,
|
||||||
Model: profile.Model,
|
Model: profile.Model,
|
||||||
Timeout: defaultInt(profile.Timeout, 120),
|
Timeout: defaultInt(profile.Timeout, 120),
|
||||||
MaxTokens: defaultInt(profile.MaxTokens, 4096),
|
MaxTokens: defaultInt(profile.MaxTokens, 4096),
|
||||||
SystemPrompt: profile.SystemPrompt,
|
ContextWindowTokens: nonNegativeInt(profile.ContextWindowTokens),
|
||||||
|
SystemPrompt: profile.SystemPrompt,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for _, tool := range tools {
|
for _, tool := range tools {
|
||||||
@@ -269,6 +273,13 @@ func defaultInt(value int, fallback int) int {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nonNegativeInt(value int) int {
|
||||||
|
if value < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
func handleAIChatAdminGetConfig(ctx *gin.Context) {
|
func handleAIChatAdminGetConfig(ctx *gin.Context) {
|
||||||
if ok, _ := requireSysAdmin(ctx); !ok {
|
if ok, _ := requireSysAdmin(ctx); !ok {
|
||||||
return
|
return
|
||||||
@@ -357,6 +368,7 @@ func saveAIChatConfig(req models.ConfigsAIChat_) error {
|
|||||||
if profile.MaxTokens <= 0 {
|
if profile.MaxTokens <= 0 {
|
||||||
profile.MaxTokens = 4096
|
profile.MaxTokens = 4096
|
||||||
}
|
}
|
||||||
|
profile.ContextWindowTokens = nonNegativeInt(profile.ContextWindowTokens)
|
||||||
if profile.Active {
|
if profile.Active {
|
||||||
if activeSet {
|
if activeSet {
|
||||||
profile.Active = false
|
profile.Active = false
|
||||||
@@ -366,15 +378,16 @@ func saveAIChatConfig(req models.ConfigsAIChat_) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tab := TabAIChatOpenAIProfile{
|
tab := TabAIChatOpenAIProfile{
|
||||||
Name: profile.Name,
|
Name: profile.Name,
|
||||||
Active: profile.Active,
|
Active: profile.Active,
|
||||||
ApiKey: profile.ApiKey,
|
ApiKey: profile.ApiKey,
|
||||||
BaseUrl: profile.BaseUrl,
|
BaseUrl: profile.BaseUrl,
|
||||||
Model: profile.Model,
|
Model: profile.Model,
|
||||||
Timeout: profile.Timeout,
|
Timeout: profile.Timeout,
|
||||||
MaxTokens: profile.MaxTokens,
|
MaxTokens: profile.MaxTokens,
|
||||||
SystemPrompt: profile.SystemPrompt,
|
ContextWindowTokens: profile.ContextWindowTokens,
|
||||||
SortOrder: i,
|
SystemPrompt: profile.SystemPrompt,
|
||||||
|
SortOrder: i,
|
||||||
}
|
}
|
||||||
if old, ok := existingByName[profile.Name]; ok {
|
if old, ok := existingByName[profile.Name]; ok {
|
||||||
tab.ID = old.ID
|
tab.ID = old.ID
|
||||||
@@ -447,14 +460,15 @@ func maskAIChatProfiles(profiles []models.ConfigsAIChatOpenAI_) []gin.H {
|
|||||||
items := make([]gin.H, 0, len(profiles))
|
items := make([]gin.H, 0, len(profiles))
|
||||||
for _, profile := range profiles {
|
for _, profile := range profiles {
|
||||||
items = append(items, gin.H{
|
items = append(items, gin.H{
|
||||||
"name": profile.Name,
|
"name": profile.Name,
|
||||||
"active": profile.Active,
|
"active": profile.Active,
|
||||||
"apiKeySet": profile.ApiKey != "",
|
"apiKeySet": profile.ApiKey != "",
|
||||||
"baseUrl": profile.BaseUrl,
|
"baseUrl": profile.BaseUrl,
|
||||||
"model": profile.Model,
|
"model": profile.Model,
|
||||||
"timeout": profile.Timeout,
|
"timeout": profile.Timeout,
|
||||||
"maxTokens": profile.MaxTokens,
|
"maxTokens": profile.MaxTokens,
|
||||||
"systemPrompt": profile.SystemPrompt,
|
"contextWindowTokens": profile.ContextWindowTokens,
|
||||||
|
"systemPrompt": profile.SystemPrompt,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return items
|
return items
|
||||||
|
|||||||
@@ -113,6 +113,8 @@
|
|||||||
"model": "Model",
|
"model": "Model",
|
||||||
"timeout": "Timeout (seconds)",
|
"timeout": "Timeout (seconds)",
|
||||||
"max_tokens": "Max Tokens",
|
"max_tokens": "Max Tokens",
|
||||||
|
"context_window_tokens": "Context Window Tokens",
|
||||||
|
"context_window_tokens_hint": "0 means unlimited. When exceeded, the system prompt and latest messages are kept while oldest Q/A turns are removed.",
|
||||||
"system_prompt": "System Prompt",
|
"system_prompt": "System Prompt",
|
||||||
"tool_router": "Tool Router",
|
"tool_router": "Tool Router",
|
||||||
"router_profile": "Router Profile",
|
"router_profile": "Router Profile",
|
||||||
@@ -132,6 +134,7 @@
|
|||||||
"error_api_key_required": "When AI is enabled, the default profile must have an API key",
|
"error_api_key_required": "When AI is enabled, the default profile must have an API key",
|
||||||
"error_timeout": "Timeout must be a positive integer",
|
"error_timeout": "Timeout must be a positive integer",
|
||||||
"error_max_tokens": "Max tokens must be a positive integer",
|
"error_max_tokens": "Max tokens must be a positive integer",
|
||||||
|
"error_context_window_tokens": "Context window tokens must be a non-negative integer",
|
||||||
"error_router_profile": "Tool router profile must exist in profile list",
|
"error_router_profile": "Tool router profile must exist in profile list",
|
||||||
"error_tool_name_required": "Tool name is required",
|
"error_tool_name_required": "Tool name is required",
|
||||||
"error_tool_name_duplicate": "Tool names must be unique"
|
"error_tool_name_duplicate": "Tool names must be unique"
|
||||||
|
|||||||
@@ -113,6 +113,8 @@
|
|||||||
"model": "模型",
|
"model": "模型",
|
||||||
"timeout": "超时(秒)",
|
"timeout": "超时(秒)",
|
||||||
"max_tokens": "最大 Token",
|
"max_tokens": "最大 Token",
|
||||||
|
"context_window_tokens": "上下文窗口 Token",
|
||||||
|
"context_window_tokens_hint": "0 表示不限制;超过后会保留系统提示词和最新消息,删除最早的问答记录。",
|
||||||
"system_prompt": "系统提示词",
|
"system_prompt": "系统提示词",
|
||||||
"tool_router": "工具路由",
|
"tool_router": "工具路由",
|
||||||
"router_profile": "路由接口",
|
"router_profile": "路由接口",
|
||||||
@@ -132,6 +134,7 @@
|
|||||||
"error_api_key_required": "启用 AI 时默认接口必须配置 API Key",
|
"error_api_key_required": "启用 AI 时默认接口必须配置 API Key",
|
||||||
"error_timeout": "超时必须是正整数",
|
"error_timeout": "超时必须是正整数",
|
||||||
"error_max_tokens": "最大 Token 必须是正整数",
|
"error_max_tokens": "最大 Token 必须是正整数",
|
||||||
|
"error_context_window_tokens": "上下文窗口 Token 必须是非负整数",
|
||||||
"error_router_profile": "工具路由接口必须来自现有接口列表",
|
"error_router_profile": "工具路由接口必须来自现有接口列表",
|
||||||
"error_tool_name_required": "工具名称不能为空",
|
"error_tool_name_required": "工具名称不能为空",
|
||||||
"error_tool_name_duplicate": "工具名称不能重复"
|
"error_tool_name_duplicate": "工具名称不能重复"
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ function normalizeProfile(profile = {}) {
|
|||||||
model: profile.model || '',
|
model: profile.model || '',
|
||||||
timeout: Number(profile.timeout || 120),
|
timeout: Number(profile.timeout || 120),
|
||||||
maxTokens: Number(profile.maxTokens || 4096),
|
maxTokens: Number(profile.maxTokens || 4096),
|
||||||
|
contextWindowTokens: Number(profile.contextWindowTokens || 0),
|
||||||
systemPrompt: profile.systemPrompt || '',
|
systemPrompt: profile.systemPrompt || '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,6 +118,7 @@ function addProfile() {
|
|||||||
model: '',
|
model: '',
|
||||||
timeout: 120,
|
timeout: 120,
|
||||||
maxTokens: 4096,
|
maxTokens: 4096,
|
||||||
|
contextWindowTokens: 0,
|
||||||
systemPrompt: '',
|
systemPrompt: '',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -178,6 +180,7 @@ function validate() {
|
|||||||
|
|
||||||
profile.timeout = Number(profile.timeout)
|
profile.timeout = Number(profile.timeout)
|
||||||
profile.maxTokens = Number(profile.maxTokens)
|
profile.maxTokens = Number(profile.maxTokens)
|
||||||
|
profile.contextWindowTokens = Number(profile.contextWindowTokens)
|
||||||
if (!Number.isInteger(profile.timeout) || profile.timeout <= 0) {
|
if (!Number.isInteger(profile.timeout) || profile.timeout <= 0) {
|
||||||
toast.error(t('aiconfig.error_timeout'))
|
toast.error(t('aiconfig.error_timeout'))
|
||||||
return false
|
return false
|
||||||
@@ -186,6 +189,10 @@ function validate() {
|
|||||||
toast.error(t('aiconfig.error_max_tokens'))
|
toast.error(t('aiconfig.error_max_tokens'))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if (!Number.isInteger(profile.contextWindowTokens) || profile.contextWindowTokens < 0) {
|
||||||
|
toast.error(t('aiconfig.error_context_window_tokens'))
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasActive) {
|
if (!hasActive) {
|
||||||
@@ -248,6 +255,7 @@ function buildPayload() {
|
|||||||
model: profile.model.trim(),
|
model: profile.model.trim(),
|
||||||
timeout: Number(profile.timeout),
|
timeout: Number(profile.timeout),
|
||||||
maxTokens: Number(profile.maxTokens),
|
maxTokens: Number(profile.maxTokens),
|
||||||
|
contextWindowTokens: Number(profile.contextWindowTokens),
|
||||||
systemPrompt: profile.systemPrompt || '',
|
systemPrompt: profile.systemPrompt || '',
|
||||||
})),
|
})),
|
||||||
toolRouter: {
|
toolRouter: {
|
||||||
@@ -360,6 +368,11 @@ async function saveConfig() {
|
|||||||
<span>{{ t('aiconfig.max_tokens') }}</span>
|
<span>{{ t('aiconfig.max_tokens') }}</span>
|
||||||
<input v-model.number="profile.maxTokens" class="input" type="number" min="1" />
|
<input v-model.number="profile.maxTokens" class="input" type="number" min="1" />
|
||||||
</label>
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>{{ t('aiconfig.context_window_tokens') }}</span>
|
||||||
|
<input v-model.number="profile.contextWindowTokens" class="input" type="number" min="0" />
|
||||||
|
<span class="text-xs font-normal text-gray-500 dark:text-dk-subtle">{{ t('aiconfig.context_window_tokens_hint') }}</span>
|
||||||
|
</label>
|
||||||
<label class="field md:col-span-2">
|
<label class="field md:col-span-2">
|
||||||
<span>{{ t('aiconfig.system_prompt') }}</span>
|
<span>{{ t('aiconfig.system_prompt') }}</span>
|
||||||
<textarea v-model="profile.systemPrompt" class="input min-h-24 resize-y" />
|
<textarea v-model="profile.systemPrompt" class="input min-h-24 resize-y" />
|
||||||
|
|||||||
Reference in New Issue
Block a user