diff --git a/app/protos/grpc.proto b/app/protos/grpc.proto index 8ea900c4c5..89dfc9ec3e 100644 --- a/app/protos/grpc.proto +++ b/app/protos/grpc.proto @@ -638,6 +638,8 @@ service Yak { // 从规则中提取数据 rpc QueryMITMRuleExtractedData(QueryMITMRuleExtractedDataRequest) returns (QueryMITMRuleExtractedDataResponse); + // 按规则名 + 提取内容聚合(JOIN http_flows,可按 Host / RuntimeId 等过滤) + rpc QueryMITMExtractedAggregate(QueryMITMExtractedAggregateRequest) returns (QueryMITMExtractedAggregateResponse); rpc ExportMITMRuleExtractedData(ExportMITMRuleExtractedDataRequest) returns (stream ExportMITMRuleExtractedDataResponse); rpc DeleteMITMRuleExtractedData(DeleteMITMRuleExtractedDataRequest) returns (Empty); rpc DeduplicateMITMRuleExtractedData(DeduplicateMITMRuleExtractedDataRequest) returns (DeduplicateMITMRuleExtractedDataResponse); @@ -1955,7 +1957,7 @@ message AIEventFilter { repeated string NodeId = 8; - bool UseOR = 9; //默认是AND关系,也就是多条件同时满足,如果UseOR是true,那是满足任一条件即可 + bool UseOR = 9; // 默认是 AND 关系,也就是多条件同时满足,如果 UseOR 是 true,那么就是满足任一条件即可 } message AIEventQueryRequest { @@ -2845,6 +2847,15 @@ message ThirdPartyApplicationConfig { string Endpoint = 14; bool EnableEndpoint = 15; repeated KVPair Headers = 16; + bool EnableThinking = 17; + // 以下为模型采样/推理相关可选参数;未设置时不写入上游请求(由模型或网关默认行为决定) + optional int64 MaxTokens = 18; + optional double Temperature = 19; + optional double TopP = 20; + optional int64 TopK = 21; + optional double FrequencyPenalty = 22; + optional string ReasoningEffort = 23; + optional bool EnableThinkingOpt = 24; } message DiagnoseNetworkRequest { @@ -3331,6 +3342,36 @@ message DeduplicateMITMRuleExtractedDataResponse { int64 DeletedCount = 1; } +// MITM 提取聚合。规则组维度:RuleGroup、RuleGroupKeyword、OnlyUncategorizedRules 互斥(实现上精确 RuleGroup 优先)。 +message QueryMITMExtractedAggregateRequest { + Paging Pagination = 1; + string HostContains = 2; + string RuntimeId = 3; + repeated string RuleVerbose = 4; + string RuleVerboseKeyword = 5; + int64 UpdatedAtSince = 6; + string RuleGroup = 7; + bool OnlyUncategorizedRules = 8; + bool IncludeDistinctRuleGroups = 9; + QueryHTTPFlowRequest HttpFlowFilter = 10; + string RuleGroupKeyword = 11; +} + +message MITMExtractedAggregateRow { + string RuleVerbose = 1; + string DisplayData = 2; + int64 HitCount = 3; + int64 LatestUpdatedAt = 4; + repeated string SampleTraceIds = 5; +} + +message QueryMITMExtractedAggregateResponse { + repeated MITMExtractedAggregateRow Data = 1; + int64 Total = 2; + Paging Pagination = 3; + repeated string DistinctRuleGroups = 4; +} + message ExportProjectRequest { string ProjectName = 1; string Password = 2; @@ -6546,6 +6587,11 @@ message GetHTTPFlowBodyByIdRequest { bool IsRisk = 5; } +message MITMExtractAggregateFlowFilterRow { + string RuleVerbose = 1; + string DisplayData = 2; +} + message QueryHTTPFlowRequest { Paging Pagination = 1; string SourceType = 2; @@ -6614,6 +6660,7 @@ message QueryHTTPFlowRequest { // 高级配置的hostname过滤(模糊匹配) repeated string HostnameFilter = 49; + repeated MITMExtractAggregateFlowFilterRow MitmExtractAggregateFilterRows = 50; } message HTTPFlowsToOnlineRequest { diff --git a/app/renderer/src/main/src/components/configNetwork/ConfigNetworkPage.module.scss b/app/renderer/src/main/src/components/configNetwork/ConfigNetworkPage.module.scss index 38a24126cf..2c9c0bd5dd 100644 --- a/app/renderer/src/main/src/components/configNetwork/ConfigNetworkPage.module.scss +++ b/app/renderer/src/main/src/components/configNetwork/ConfigNetworkPage.module.scss @@ -440,6 +440,28 @@ .config-form { padding: 24px 24px 0 24px; .ai-third-party-application-config-collapse { + .panel-heard { + display: flex; + align-items: center; + gap: 4px; + font-size: 12px; + line-height: 16px; + .title { + font-weight: 500; + color: var(--Colors-Use-Neutral-Text-1-Title); + } + .tip { + color: var(--Colors-Use-Neutral-Text-4-Help-text); + } + } + :global(.ant-collapse-header:hover) { + .panel-heard { + background-color: var(--Colors-Use-Basic-Background); + .title { + color: var(--Colors-Use-Main-Primary); + } + } + } :global { .ant-collapse-item { background-color: var(--Colors-Use-Basic-Background); diff --git a/app/renderer/src/main/src/components/configNetwork/ConfigNetworkPage.tsx b/app/renderer/src/main/src/components/configNetwork/ConfigNetworkPage.tsx index 5378416a3f..39964a1f64 100644 --- a/app/renderer/src/main/src/components/configNetwork/ConfigNetworkPage.tsx +++ b/app/renderer/src/main/src/components/configNetwork/ConfigNetworkPage.tsx @@ -141,6 +141,20 @@ export interface ThirdPartyApplicationConfig { Endpoint?: string EnableEndpoint?: boolean Headers?: KVPair[] + /**为空,不传给后端 */ + MaxTokens?: number + /**为空,不传给后端 */ + Temperature?: number + /**为空,不传给后端 */ + TopP?: number + /**为空,不传给后端 */ + TopK?: number + /**为空,不传给后端 */ + FrequencyPenalty?: number + /**为空,不传给后端 */ + ReasoningEffort?: string + /**为空,不传给后端 */ + EnableThinkingOpt?: boolean } type TenumBuffer = Buffer | Uint8Array diff --git a/app/renderer/src/main/src/components/configNetwork/NewThirdPartyApplicationConfig.tsx b/app/renderer/src/main/src/components/configNetwork/NewThirdPartyApplicationConfig.tsx index 1b3a61f6fd..796cb55654 100644 --- a/app/renderer/src/main/src/components/configNetwork/NewThirdPartyApplicationConfig.tsx +++ b/app/renderer/src/main/src/components/configNetwork/NewThirdPartyApplicationConfig.tsx @@ -35,6 +35,7 @@ import classNames from 'classnames' import { TFunction, useI18nNamespaces } from '@/i18n/useI18nNamespaces' import { OutlineClipboardcopyIcon } from '@/assets/icon/outline' import { setClipboardText } from '@/utils/clipboard' +import { YakitInputNumber } from '../yakitUI/YakitInputNumber/YakitInputNumber' const { ipcRenderer } = window.require('electron') export interface ThirdPartyAppConfigItemTemplate { @@ -991,7 +992,7 @@ export const NewAIThirdPartyApplicationConfigBase: React.FC { @@ -1048,7 +1049,7 @@ export const NewAIThirdPartyApplicationConfigBase: React.FC - + {/* 可选的表单项 */} {renderOptionalFormItems()} @@ -1084,7 +1085,53 @@ export const NewAIThirdPartyApplicationConfigBase: React.FC - + + + 模型配置 + 以下值可以为空,为空代表不设置 + + } + key="2" + > + + + + + + + + + + + + + + + + + + + + + +
{footer}
diff --git a/app/renderer/src/main/src/pages/ai-agent/aiModelList/aiModelForm/AIModelForm.tsx b/app/renderer/src/main/src/pages/ai-agent/aiModelList/aiModelForm/AIModelForm.tsx index aea079d493..bbfa360039 100644 --- a/app/renderer/src/main/src/pages/ai-agent/aiModelList/aiModelForm/AIModelForm.tsx +++ b/app/renderer/src/main/src/pages/ai-agent/aiModelList/aiModelForm/AIModelForm.tsx @@ -32,7 +32,7 @@ import { AIModelTypeEnum, AIModelTypeInterFileNameEnum } from '../../defaultCons import { YakitInput } from '@/components/yakitUI/YakitInput/YakitInput' import { yakitNotify } from '@/utils/notification' import emiter from '@/utils/eventBus/eventBus' -import { cloneDeep } from 'lodash' +import { cloneDeep, has, isNil } from 'lodash' import { ThirdPartyApplicationConfig } from '@/components/configNetwork/ConfigNetworkPage' import { showYakitModal } from '@/components/yakitUI/YakitModal/YakitModalConfirm' import styles from './AIModelForm.module.scss' @@ -165,8 +165,61 @@ export const buildAIConfigHealthCheckFormValues = (config: ThirdPartyApplication endpoint: config.Endpoint ?? '', enable_endpoint: config.EnableEndpoint ?? false, Headers: config.Headers ?? [], + EnableThinkingOpt: has(config, 'EnableThinkingOpt') + ? `${config.EnableThinkingOpt === true ? 'open' : 'close'}` + : 'no-set', + MaxTokens: config?.MaxTokens, + Temperature: config?.Temperature, + TopP: config?.TopP, + TopK: config?.TopK, + FrequencyPenalty: config?.FrequencyPenalty, + ReasoningEffort: config?.ReasoningEffort || 'no-set', } as AIThirdPartyApplicationConfig } + +const formValueToAIConfigProvider = (res) => { + const date: ThirdPartyApplicationConfig = { + Type: res.Type, + APIKey: res.api_key, + APIType: normalizeAIAPIType(res.api_type), + Domain: res.domain, + Proxy: res.proxy, + NoHttps: res.no_https, + ExtraParams: [], + BaseURL: res.base_url, + Endpoint: res.endpoint, + EnableEndpoint: res.enable_endpoint, + Headers: res.Headers, + /** 下面这些字段ai中没有 */ + // UserIdentifier: "", + // UserSecret: "", + // Namespace: "", + // WebhookURL: "", + // Disabled: false + } + if (has(res, 'EnableThinkingOpt') && res.EnableThinkingOpt !== 'no-set') { + date.EnableThinkingOpt = res.EnableThinkingOpt === 'open' + } + if (has(res, 'MaxTokens') && !isNil(res.MaxTokens)) { + date.MaxTokens = res.MaxTokens + } + if (has(res, 'Temperature') && !isNil(res.Temperature)) { + date.Temperature = res.Temperature + } + if (has(res, 'TopP') && !isNil(res.TopP)) { + date.TopP = res.TopP + } + if (has(res, 'TopK') && !isNil(res.TopK)) { + date.TopK = res.TopK + } + if (has(res, 'FrequencyPenalty') && !isNil(res.FrequencyPenalty)) { + date.FrequencyPenalty = res.FrequencyPenalty + } + if (has(res, 'ReasoningEffort') && res.ReasoningEffort !== 'no-set') { + date.ReasoningEffort = res.ReasoningEffort + } + return date +} export const AIModelForm: React.FC = React.memo((props) => { const { item, aiModelType, onSuccess, onClose } = props @@ -229,23 +282,7 @@ export const AIModelForm: React.FC = React.memo((props) => { const newItem: AIModelConfig = { ProviderId: res.api_key_id, Provider: { - Type: res.Type, - APIKey: res.api_key, - APIType: normalizeAIAPIType(res.api_type), - Domain: res.domain, - Proxy: res.proxy, - NoHttps: res.no_https, - ExtraParams: [], - BaseURL: res.base_url, - Endpoint: res.endpoint, - EnableEndpoint: res.enable_endpoint, - Headers: res.Headers, - /** 下面这些字段ai中没有 */ - // UserIdentifier: "", - // UserSecret: "", - // Namespace: "", - // WebhookURL: "", - // Disabled: false + ...formValueToAIConfigProvider(res), }, ModelName: res.model, ExtraParams: [],