@jsonstudio/llms 0.6.802 → 0.6.954
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bridge/routecodex-adapter.d.ts +74 -0
- package/dist/config-unified/enhanced-path-resolver.d.ts +5 -0
- package/dist/config-unified/unified-config.d.ts +26 -0
- package/dist/conversion/codec-registry.d.ts +10 -0
- package/dist/conversion/codecs/gemini-openai-codec.d.ts +16 -0
- package/dist/conversion/codecs/openai-openai-codec.d.ts +12 -0
- package/dist/conversion/codecs/responses-openai-codec.d.ts +12 -0
- package/dist/conversion/compat/profiles/chat-gemini.json +12 -0
- package/dist/conversion/config/config-manager.d.ts +212 -0
- package/dist/conversion/hub/config/types.d.ts +26 -0
- package/dist/conversion/hub/core/detour-registry.d.ts +9 -0
- package/dist/conversion/hub/core/hub-context.d.ts +21 -0
- package/dist/conversion/hub/core/index.d.ts +3 -0
- package/dist/conversion/hub/core/stage-driver.d.ts +30 -0
- package/dist/conversion/hub/format-adapters/anthropic-format-adapter.d.ts +16 -0
- package/dist/conversion/hub/format-adapters/chat-format-adapter.d.ts +17 -0
- package/dist/conversion/hub/format-adapters/gemini-format-adapter.d.ts +16 -0
- package/dist/conversion/hub/format-adapters/index.d.ts +21 -0
- package/dist/conversion/hub/hub-feature.d.ts +1 -0
- package/dist/conversion/hub/node-support.d.ts +19 -0
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +11 -0
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +3 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +7 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +113 -17
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +6 -3
- package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +4 -0
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +23 -1
- package/dist/conversion/hub/pipelines/inbound.d.ts +22 -0
- package/dist/conversion/hub/pipelines/outbound.d.ts +22 -0
- package/dist/conversion/hub/policy/policy-engine.d.ts +46 -0
- package/dist/conversion/hub/policy/policy-engine.js +176 -0
- package/dist/conversion/hub/policy/protocol-spec.d.ts +50 -0
- package/dist/conversion/hub/policy/protocol-spec.js +105 -0
- package/dist/conversion/hub/process/chat-process.d.ts +32 -0
- package/dist/conversion/hub/registry.d.ts +28 -0
- package/dist/conversion/hub/response/chat-response-utils.d.ts +6 -0
- package/dist/conversion/hub/response/provider-response.js +31 -0
- package/dist/conversion/hub/semantic-mappers/chat-mapper.js +32 -1
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.d.ts +7 -0
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +96 -1
- package/dist/conversion/hub/semantic-mappers/index.d.ts +4 -0
- package/dist/conversion/hub/semantic-mappers/responses-mapper.d.ts +21 -0
- package/dist/conversion/hub/standardized-bridge.d.ts +12 -0
- package/dist/conversion/hub/types/chat-schema.d.ts +112 -0
- package/dist/conversion/hub/types/errors.d.ts +5 -0
- package/dist/conversion/hub/types/format-envelope.d.ts +7 -0
- package/dist/conversion/hub/types/index.d.ts +6 -0
- package/dist/conversion/hub/types/json.d.ts +9 -0
- package/dist/conversion/hub/types/node.d.ts +31 -0
- package/dist/conversion/responses/responses-openai-bridge.js +263 -10
- package/dist/conversion/schema-validator.d.ts +7 -0
- package/dist/conversion/shared/args-mapping.d.ts +18 -0
- package/dist/conversion/shared/chat-request-filters.d.ts +9 -0
- package/dist/conversion/shared/errors.d.ts +1 -1
- package/dist/conversion/shared/gemini-tool-utils.js +105 -1
- package/dist/conversion/shared/jsonish.d.ts +3 -0
- package/dist/conversion/shared/mcp-injection.d.ts +2 -0
- package/dist/conversion/shared/media.d.ts +1 -0
- package/dist/conversion/shared/openai-message-normalize.d.ts +1 -0
- package/dist/conversion/shared/payload-budget.d.ts +13 -0
- package/dist/conversion/shared/reasoning-mapping.d.ts +5 -0
- package/dist/conversion/shared/responses-request-adapter.d.ts +1 -28
- package/dist/conversion/shared/responses-request-adapter.js +1 -430
- package/dist/conversion/shared/snapshot-hooks.js +58 -3
- package/dist/conversion/shared/tool-governor.js +8 -2
- package/dist/conversion/shared/tool-harvester.d.ts +31 -0
- package/dist/conversion/shared/tool-mapping.js +10 -29
- package/dist/conversion/types.d.ts +33 -0
- package/dist/filters/builtin/add-fields-filter.d.ts +8 -0
- package/dist/filters/builtin/blacklist-filter.d.ts +8 -0
- package/dist/filters/builtin/whitelist-filter.d.ts +8 -0
- package/dist/filters/engine.d.ts +16 -0
- package/dist/filters/special/request-tool-choice-policy.d.ts +11 -0
- package/dist/filters/special/response-finish-invariants.d.ts +11 -0
- package/dist/filters/special/response-openai-to-responses-bridge.d.ts +13 -0
- package/dist/filters/special/response-tool-arguments-blacklist.d.ts +12 -0
- package/dist/filters/special/response-tool-arguments-schema-converge.d.ts +13 -0
- package/dist/filters/special/response-tool-arguments-stringify.d.ts +9 -0
- package/dist/filters/special/response-tool-arguments-whitelist.d.ts +11 -0
- package/dist/filters/special/tool-filter-hooks.d.ts +19 -0
- package/dist/filters/special/tool-post-constraints.d.ts +31 -0
- package/dist/filters/types.d.ts +68 -0
- package/dist/filters/utils/fieldmap-loader.d.ts +2 -0
- package/dist/filters/utils/snapshot-writer.d.ts +10 -0
- package/dist/guidance/index.d.ts +3 -0
- package/dist/guidance/index.js +78 -83
- package/dist/http/sse-response.d.ts +22 -0
- package/dist/router/virtual-router/bootstrap.d.ts +6 -0
- package/dist/router/virtual-router/bootstrap.js +49 -5
- package/dist/router/virtual-router/classifier.d.ts +10 -0
- package/dist/router/virtual-router/engine-selection.js +98 -11
- package/dist/router/virtual-router/engine.js +177 -31
- package/dist/router/virtual-router/error-center.d.ts +10 -0
- package/dist/router/virtual-router/features.d.ts +3 -0
- package/dist/router/virtual-router/routing-instructions.d.ts +23 -1
- package/dist/router/virtual-router/routing-instructions.js +120 -30
- package/dist/router/virtual-router/types.d.ts +11 -0
- package/dist/servertool/engine.js +192 -17
- package/dist/servertool/handlers/apply-patch-guard.js +269 -0
- package/dist/servertool/handlers/exec-command-guard.js +558 -0
- package/dist/servertool/handlers/followup-message-trimmer.d.ts +16 -0
- package/dist/servertool/handlers/followup-message-trimmer.js +198 -0
- package/dist/servertool/handlers/followup-request-builder.d.ts +17 -0
- package/dist/servertool/handlers/followup-request-builder.js +122 -0
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +252 -51
- package/dist/servertool/handlers/iflow-model-error-retry.js +12 -22
- package/dist/servertool/handlers/stop-message-auto.js +237 -75
- package/dist/servertool/handlers/vision.js +15 -27
- package/dist/servertool/handlers/web-search.js +17 -43
- package/dist/servertool/server-side-tools.d.ts +3 -0
- package/dist/servertool/server-side-tools.js +3 -0
- package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.d.ts +2 -1
- package/dist/sse/json-to-sse/chat-json-to-sse-converter.d.ts +80 -0
- package/dist/sse/json-to-sse/event-generators/chat.d.ts +55 -0
- package/dist/sse/json-to-sse/event-generators/responses.d.ts +99 -0
- package/dist/sse/json-to-sse/gemini-json-to-sse-converter.d.ts +2 -1
- package/dist/sse/json-to-sse/responses-json-to-sse-converter.d.ts +80 -0
- package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.d.ts +1 -1
- package/dist/sse/json-to-sse/sequencers/chat-sequencer.d.ts +2 -2
- package/dist/sse/json-to-sse/sequencers/gemini-sequencer.d.ts +1 -1
- package/dist/sse/json-to-sse/sequencers/responses-sequencer.d.ts +40 -0
- package/dist/sse/shared/chat-serializer.d.ts +4 -0
- package/dist/sse/shared/constants.d.ts +272 -0
- package/dist/sse/shared/serializers/anthropic-event-serializer.d.ts +1 -1
- package/dist/sse/shared/serializers/base-serializer.d.ts +158 -0
- package/dist/sse/shared/serializers/chat-event-serializer.d.ts +82 -0
- package/dist/sse/shared/serializers/gemini-event-serializer.d.ts +1 -1
- package/dist/sse/shared/serializers/index.d.ts +2 -1
- package/dist/sse/shared/serializers/responses-event-serializer.d.ts +123 -0
- package/dist/sse/shared/serializers/types.d.ts +51 -0
- package/dist/sse/shared/utils.d.ts +254 -0
- package/dist/sse/shared/writer.d.ts +2 -2
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -1
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.d.ts +1 -1
- package/dist/sse/sse-to-json/builders/response-builder.d.ts +1 -1
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +2 -1
- package/dist/sse/sse-to-json/gemini-sse-to-json-converter.d.ts +2 -1
- package/dist/sse/sse-to-json/parsers/sse-parser.d.ts +73 -0
- package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -1
- package/dist/sse/types/chat-types.d.ts +1 -1
- package/dist/sse/types/responses-types.d.ts +1 -1
- package/dist/tools/apply-patch/execution-capturer.d.ts +13 -0
- package/dist/tools/apply-patch/execution-capturer.js +158 -0
- package/dist/tools/apply-patch/regression-capturer.d.ts +1 -0
- package/dist/tools/apply-patch/regression-capturer.js +5 -4
- package/dist/tools/apply-patch/structured.js +109 -13
- package/dist/tools/apply-patch/validator.js +112 -18
- package/dist/tools/tool-registry.d.ts +8 -0
- package/dist/tools/tool-registry.js +2 -1
- package/package.json +4 -4
- package/dist/conversion/compat/actions/apply-patch-format-fixer.js +0 -233
- package/dist/conversion/config/compat-profiles.json +0 -38
- package/dist/conversion/hub/pipeline/context-limit.d.ts +0 -13
- package/dist/conversion/hub/pipeline/context-limit.js +0 -55
- package/dist/conversion/hub/response/server-side-tools.d.ts +0 -26
- package/dist/conversion/hub/response/server-side-tools.js +0 -383
- package/dist/conversion/shared/bridge-conversation-store.d.ts +0 -41
- package/dist/conversion/shared/bridge-conversation-store.js +0 -279
- package/dist/conversion/shared/bridge-request-adapter.d.ts +0 -28
- package/dist/conversion/shared/bridge-request-adapter.js +0 -430
- package/dist/conversion/shared/responses-id-utils.js +0 -42
- package/dist/conversion/shared/responses-instructions.js +0 -113
- package/dist/conversion/shared/responses-message-utils.d.ts +0 -15
- package/dist/conversion/shared/responses-message-utils.js +0 -206
- package/dist/conversion/shared/responses-metadata.js +0 -1
- package/dist/conversion/shared/responses-output-utils.d.ts +0 -7
- package/dist/conversion/shared/responses-output-utils.js +0 -108
- package/dist/conversion/shared/responses-types.d.ts +0 -33
- package/dist/conversion/shared/tool-normalizers.d.ts +0 -4
- package/dist/conversion/shared/tool-normalizers.js +0 -84
- package/dist/filters/special/request-streaming-to-nonstreaming.d.ts +0 -13
- package/dist/filters/special/request-streaming-to-nonstreaming.js +0 -39
- package/dist/filters/special/response-apply-patch-toon-decode.d.ts +0 -23
- package/dist/filters/special/response-apply-patch-toon-decode.js +0 -460
- package/dist/filters/special/response-tool-arguments-toon-decode.d.ts +0 -10
- package/dist/filters/special/response-tool-arguments-toon-decode.js +0 -154
- package/dist/servertool/flow-types.d.ts +0 -40
- package/dist/servertool/flow-types.js +0 -1
- package/dist/servertool/orchestration-types.d.ts +0 -33
- package/dist/servertool/orchestration-types.js +0 -1
- package/dist/servertool/vision-tool.d.ts +0 -2
- package/dist/servertool/vision-tool.js +0 -185
- package/dist/tools/patch-args-normalizer.d.ts +0 -15
- package/dist/tools/patch-args-normalizer.js +0 -472
- package/dist/utils/toon.d.ts +0 -4
- package/dist/utils/toon.js +0 -75
- /package/dist/{conversion/compat/actions/apply-patch-format-fixer.d.ts → servertool/handlers/apply-patch-guard.d.ts} +0 -0
- /package/dist/{conversion/shared/responses-types.js → servertool/handlers/exec-command-guard.d.ts} +0 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
|
|
2
|
+
export declare class AddFieldsFilter implements Filter<JsonObject> {
|
|
3
|
+
readonly name = "add_fields";
|
|
4
|
+
readonly stage: any;
|
|
5
|
+
private readonly fields;
|
|
6
|
+
constructor(stage: FilterContext['stage'], fields: Record<string, unknown>);
|
|
7
|
+
apply(input: JsonObject): FilterResult<JsonObject>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
|
|
2
|
+
export declare class BlacklistFilter implements Filter<JsonObject> {
|
|
3
|
+
readonly name = "blacklist";
|
|
4
|
+
readonly stage: any;
|
|
5
|
+
private readonly deny;
|
|
6
|
+
constructor(stage: FilterContext['stage'], deny: string[]);
|
|
7
|
+
apply(input: JsonObject): FilterResult<JsonObject>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
|
|
2
|
+
export declare class WhitelistFilter implements Filter<JsonObject> {
|
|
3
|
+
readonly name = "whitelist";
|
|
4
|
+
readonly stage: any;
|
|
5
|
+
private readonly allow;
|
|
6
|
+
constructor(stage: FilterContext['stage'], allow: string[]);
|
|
7
|
+
apply(input: JsonObject): FilterResult<JsonObject>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Filter, FilterContext, FieldMapConfig, JsonObject } from './types.js';
|
|
2
|
+
export declare class FilterEngine {
|
|
3
|
+
private readonly filters;
|
|
4
|
+
private fieldMap?;
|
|
5
|
+
private readonly transforms;
|
|
6
|
+
registerFilter(filter: Filter): void;
|
|
7
|
+
setFieldMap(config: FieldMapConfig): void;
|
|
8
|
+
registerTransform(name: string, fn: (v: unknown) => unknown): void;
|
|
9
|
+
run(stage: FilterContext['stage'], payload: JsonObject, ctxBase: Omit<FilterContext, 'stage'>): Promise<JsonObject>;
|
|
10
|
+
private applyFieldMap;
|
|
11
|
+
private coerce;
|
|
12
|
+
private deepClone;
|
|
13
|
+
private mutateWildcardPath;
|
|
14
|
+
private getByPath;
|
|
15
|
+
private setByPath;
|
|
16
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Tool choice policy:
|
|
4
|
+
* - If tools exist and no tool_choice, set tool_choice='auto'
|
|
5
|
+
* - If no tools, remove tool_choice
|
|
6
|
+
*/
|
|
7
|
+
export declare class RequestToolChoicePolicyFilter implements Filter<JsonObject> {
|
|
8
|
+
readonly name = "request_tool_choice_policy";
|
|
9
|
+
readonly stage: FilterContext['stage'];
|
|
10
|
+
apply(input: JsonObject): FilterResult<JsonObject>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Ensure finish_reason and content invariants on response side:
|
|
4
|
+
* - If tool_calls present, finish_reason defaults to 'tool_calls'
|
|
5
|
+
* - If tool_calls present, message.content=null (Chat path)
|
|
6
|
+
*/
|
|
7
|
+
export declare class ResponseFinishInvariantsFilter implements Filter<JsonObject> {
|
|
8
|
+
readonly name = "response_finish_invariants";
|
|
9
|
+
readonly stage: FilterContext['stage'];
|
|
10
|
+
apply(input: JsonObject): FilterResult<JsonObject>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Filter, FilterContext, JsonObject, FilterResult } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* ResponseOpenAIToResponsesBridgeFilter
|
|
4
|
+
* - 将 OpenAI Chat 形状的响应桥接为 OpenAI Responses 形状
|
|
5
|
+
* - 与 Anthropic 并行:使用 filter 进行形状/协议转换(而非 endpoint 分支)
|
|
6
|
+
* - 阶段:response_post(在基础规范化之后执行)
|
|
7
|
+
*/
|
|
8
|
+
export declare class ResponseOpenAIToResponsesBridgeFilter implements Filter<JsonObject> {
|
|
9
|
+
readonly name = "response_openai_to_responses_bridge";
|
|
10
|
+
readonly stage: FilterContext['stage'];
|
|
11
|
+
apply(input: JsonObject, ctx: FilterContext): Promise<FilterResult<JsonObject>>;
|
|
12
|
+
}
|
|
13
|
+
export default ResponseOpenAIToResponsesBridgeFilter;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* ResponseToolArgumentsBlacklistFilter
|
|
4
|
+
* - Remove keys listed in ROUTECODEX_TOOLARGS_BLACKLIST from tool_calls[].function.arguments (after decode).
|
|
5
|
+
* - Default: empty blacklist (no-op). Enables incremental hardening without risking valid params.
|
|
6
|
+
* Stage: response_pre
|
|
7
|
+
*/
|
|
8
|
+
export declare class ResponseToolArgumentsBlacklistFilter implements Filter<JsonObject> {
|
|
9
|
+
readonly name = "response_tool_arguments_blacklist";
|
|
10
|
+
readonly stage: FilterContext['stage'];
|
|
11
|
+
apply(input: JsonObject): FilterResult<JsonObject>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* ResponseToolArgumentsSchemaConvergeFilter
|
|
4
|
+
* - If tool schemas are available on context (context.toolSchemas[name] = JSONSchema),
|
|
5
|
+
* keep only keys defined in schema.properties for each tool call arguments.
|
|
6
|
+
* - Does not validate or coerce types; only converges keys to schema surface.
|
|
7
|
+
* - Stage: response_pre
|
|
8
|
+
*/
|
|
9
|
+
export declare class ResponseToolArgumentsSchemaConvergeFilter implements Filter<JsonObject> {
|
|
10
|
+
readonly name = "response_tool_arguments_schema_converge";
|
|
11
|
+
readonly stage: FilterContext['stage'];
|
|
12
|
+
apply(input: JsonObject, context?: FilterContext): FilterResult<JsonObject>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Ensure choices[*].message.tool_calls[].function.arguments is a JSON string.
|
|
4
|
+
*/
|
|
5
|
+
export declare class ResponseToolArgumentsStringifyFilter implements Filter<JsonObject> {
|
|
6
|
+
readonly name = "response_tool_arguments_stringify";
|
|
7
|
+
readonly stage: FilterContext['stage'];
|
|
8
|
+
apply(input: JsonObject): FilterResult<JsonObject>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Whitelist tool function arguments keys after decode to match schema (e.g., keep only 'command'/'workdir').
|
|
4
|
+
* Stage: response_pre. Runs after TOON decode and before stringify.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ResponseToolArgumentsWhitelistFilter implements Filter<JsonObject> {
|
|
7
|
+
readonly name = "response_tool_arguments_whitelist";
|
|
8
|
+
readonly stage: FilterContext['stage'];
|
|
9
|
+
private getWhitelist;
|
|
10
|
+
apply(input: JsonObject, context?: FilterContext): FilterResult<JsonObject>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
|
|
2
|
+
interface ToolFilterConfigCategoryPolicy {
|
|
3
|
+
mode?: 'allow' | 'block' | 'require_content';
|
|
4
|
+
requireContentTypes?: string[];
|
|
5
|
+
}
|
|
6
|
+
interface ToolFilterConfig {
|
|
7
|
+
categories?: {
|
|
8
|
+
vision?: ToolFilterConfigCategoryPolicy;
|
|
9
|
+
mcp?: ToolFilterConfigCategoryPolicy;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export declare function setGlobalToolFilterConfig(cfg: ToolFilterConfig | undefined): void;
|
|
13
|
+
export declare function getGlobalToolFilterConfig(): ToolFilterConfig;
|
|
14
|
+
export declare class ToolFilterHookFilter implements Filter<JsonObject> {
|
|
15
|
+
readonly name = "tool_filter_hook";
|
|
16
|
+
readonly stage: FilterContext['stage'];
|
|
17
|
+
apply(input: JsonObject, ctx: FilterContext): FilterResult<JsonObject>;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Filter, FilterContext, FilterResult, JsonObject } from '../types.js';
|
|
2
|
+
type ConstraintMatchCondition = 'missing' | 'empty' | 'missingOrEmpty';
|
|
3
|
+
interface ToolConstraintRule {
|
|
4
|
+
id: string;
|
|
5
|
+
direction: 'request' | 'response' | 'both';
|
|
6
|
+
providerProtocol?: string;
|
|
7
|
+
providerId?: string;
|
|
8
|
+
modelId?: string;
|
|
9
|
+
toolNamePattern?: string;
|
|
10
|
+
path: string;
|
|
11
|
+
when: ConstraintMatchCondition;
|
|
12
|
+
action: 'warn' | 'patch' | 'drop';
|
|
13
|
+
patchText?: string;
|
|
14
|
+
}
|
|
15
|
+
interface ToolGovernanceConstraintsConfig {
|
|
16
|
+
rules: ToolConstraintRule[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Tool post-constraints filter(请求/响应阶段的最后一小步约束):
|
|
20
|
+
* - 默认只记录(warn),不修改 payload;
|
|
21
|
+
* - 修剪/修复行为完全由配置驱动(ToolGovernanceConstraintsConfig);
|
|
22
|
+
* - 当前仅实现缺失/空 description 的简单场景,后续可按配置扩展。
|
|
23
|
+
*/
|
|
24
|
+
export declare class ToolPostConstraintsFilter implements Filter<JsonObject> {
|
|
25
|
+
readonly name = "tool_post_constraints";
|
|
26
|
+
readonly stage: FilterContext['stage'];
|
|
27
|
+
private readonly config;
|
|
28
|
+
constructor(stage: FilterContext['stage'], config?: ToolGovernanceConstraintsConfig);
|
|
29
|
+
apply(input: JsonObject, ctx: FilterContext): FilterResult<JsonObject>;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export type JsonObject = Record<string, unknown>;
|
|
2
|
+
export type FilterStage = 'request_pre' | 'request_map' | 'request_post' | 'request_finalize' | 'response_pre' | 'response_map' | 'response_post' | 'response_finalize';
|
|
3
|
+
export interface FilterContext {
|
|
4
|
+
requestId?: string;
|
|
5
|
+
model?: string;
|
|
6
|
+
endpoint?: string;
|
|
7
|
+
provider?: string;
|
|
8
|
+
profile?: string;
|
|
9
|
+
stage: FilterStage;
|
|
10
|
+
stream?: boolean;
|
|
11
|
+
toolFilterHints?: ToolFilterHints;
|
|
12
|
+
debug?: {
|
|
13
|
+
emit?: (event: string, data: unknown) => void;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface FilterResult<T = JsonObject> {
|
|
17
|
+
ok: boolean;
|
|
18
|
+
data: T;
|
|
19
|
+
warnings?: string[];
|
|
20
|
+
metrics?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
export interface Filter<T = JsonObject> {
|
|
23
|
+
readonly name: string;
|
|
24
|
+
readonly stage: FilterStage;
|
|
25
|
+
apply(input: T, ctx: FilterContext): Promise<FilterResult<T>> | FilterResult<T>;
|
|
26
|
+
}
|
|
27
|
+
export type ToolFilterAction = 'allow' | 'block' | 'rewrite';
|
|
28
|
+
export interface ToolFilterDecision {
|
|
29
|
+
name: string;
|
|
30
|
+
action: ToolFilterAction;
|
|
31
|
+
reason?: string;
|
|
32
|
+
category?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface ToolFilterHints {
|
|
35
|
+
requestedDecisions?: ToolFilterDecision[];
|
|
36
|
+
categories?: {
|
|
37
|
+
vision?: boolean;
|
|
38
|
+
mcp?: boolean;
|
|
39
|
+
webSearch?: boolean;
|
|
40
|
+
codeExecution?: boolean;
|
|
41
|
+
fileSearch?: boolean;
|
|
42
|
+
dataAnalysis?: boolean;
|
|
43
|
+
};
|
|
44
|
+
categoryOverrides?: {
|
|
45
|
+
vision?: 'allow' | 'block' | 'require_content';
|
|
46
|
+
mcp?: 'allow' | 'block';
|
|
47
|
+
};
|
|
48
|
+
decided?: ToolFilterDecision[];
|
|
49
|
+
}
|
|
50
|
+
export interface FieldMapRule {
|
|
51
|
+
sourcePath: string;
|
|
52
|
+
targetPath: string;
|
|
53
|
+
type?: 'string' | 'number' | 'boolean' | 'object' | 'array';
|
|
54
|
+
transform?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface FieldMapConfig {
|
|
57
|
+
request?: FieldMapRule[];
|
|
58
|
+
response?: FieldMapRule[];
|
|
59
|
+
}
|
|
60
|
+
export interface WhitelistConfig {
|
|
61
|
+
allow: string[];
|
|
62
|
+
}
|
|
63
|
+
export interface BlacklistConfig {
|
|
64
|
+
deny: string[];
|
|
65
|
+
}
|
|
66
|
+
export interface AddFieldsConfig {
|
|
67
|
+
fields: Record<string, unknown>;
|
|
68
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FilterStage } from '../types.js';
|
|
2
|
+
export declare function writeFilterSnapshot(options: {
|
|
3
|
+
requestId?: string;
|
|
4
|
+
endpoint?: string;
|
|
5
|
+
profile?: string;
|
|
6
|
+
stage: FilterStage;
|
|
7
|
+
name?: string;
|
|
8
|
+
tag?: string;
|
|
9
|
+
data: unknown;
|
|
10
|
+
}): Promise<void>;
|
package/dist/guidance/index.js
CHANGED
|
@@ -19,6 +19,58 @@ function appendOnce(desc, guidance, marker) {
|
|
|
19
19
|
return base;
|
|
20
20
|
return base ? `${base}\n\n${guidance}` : guidance;
|
|
21
21
|
}
|
|
22
|
+
function buildApplyPatchGuidanceText() {
|
|
23
|
+
const marker = '[Codex ApplyPatch Guidance]';
|
|
24
|
+
const text = [
|
|
25
|
+
marker,
|
|
26
|
+
'Send ONLY patch text (do not describe tools in prose).',
|
|
27
|
+
'',
|
|
28
|
+
'✅ Supported patch text formats:',
|
|
29
|
+
' A) Internal patch format: "*** Begin Patch" / "*** End Patch" + "*** Update/Add/Delete File"',
|
|
30
|
+
' B) GNU unified diff: "diff --git", "---/+++", "@@" hunks, /dev/null for new files',
|
|
31
|
+
'',
|
|
32
|
+
'✅ Create vs update:',
|
|
33
|
+
' - "*** Update File" MUST target an existing file (no implicit create).',
|
|
34
|
+
' - To create a file, use "*** Add File:" (or GNU diff with --- /dev/null).',
|
|
35
|
+
'',
|
|
36
|
+
'✅ Common pitfalls:',
|
|
37
|
+
' - In "*** Add File", EVERY content line must start with "+".',
|
|
38
|
+
' - In "*** Update File" hunks, EVERY line must start with one of: " " (context), "-" (remove), "+" (add), or "@@" (hunk header).',
|
|
39
|
+
'',
|
|
40
|
+
'✅ Minimal examples (patch text):',
|
|
41
|
+
'',
|
|
42
|
+
'1) Create a new file',
|
|
43
|
+
'```patch',
|
|
44
|
+
'*** Begin Patch',
|
|
45
|
+
'*** Add File: path/to/new.txt',
|
|
46
|
+
'+Line 1',
|
|
47
|
+
'+Line 2',
|
|
48
|
+
'*** End Patch',
|
|
49
|
+
'```',
|
|
50
|
+
'',
|
|
51
|
+
'2) Replace one line in an existing file (keep surrounding context)',
|
|
52
|
+
'```patch',
|
|
53
|
+
'*** Begin Patch',
|
|
54
|
+
'*** Update File: path/to/existing.txt',
|
|
55
|
+
'@@',
|
|
56
|
+
' Line 1',
|
|
57
|
+
'-Line 2',
|
|
58
|
+
'+Modified Line 2',
|
|
59
|
+
' Line 3',
|
|
60
|
+
'*** End Patch',
|
|
61
|
+
'```',
|
|
62
|
+
'',
|
|
63
|
+
'3) Delete a file',
|
|
64
|
+
'```patch',
|
|
65
|
+
'*** Begin Patch',
|
|
66
|
+
'*** Delete File: path/to/obsolete.txt',
|
|
67
|
+
'*** End Patch',
|
|
68
|
+
'```',
|
|
69
|
+
'',
|
|
70
|
+
'Paths must stay relative to the workspace root (no leading "/" or drive letters).'
|
|
71
|
+
].join('\n');
|
|
72
|
+
return { marker, text };
|
|
73
|
+
}
|
|
22
74
|
function augmentShell(fn) {
|
|
23
75
|
const marker = '[Codex Shell Guidance]';
|
|
24
76
|
const guidance = [
|
|
@@ -59,53 +111,20 @@ function augmentShell(fn) {
|
|
|
59
111
|
fn.description = appendOnce(fn.description, guidance, marker);
|
|
60
112
|
}
|
|
61
113
|
function augmentApplyPatch(fn) {
|
|
62
|
-
const marker
|
|
63
|
-
const guidance = [
|
|
64
|
-
marker,
|
|
65
|
-
'Provide structured edits instead of raw diff text. Always send JSON arguments with a `changes` array.',
|
|
66
|
-
'Each change describes one operation, e.g. `{ "file": "src/foo.ts", "kind": "insert_after", "anchor": "const foo = 1;", "lines": ["const bar = 2;"] }`.',
|
|
67
|
-
'Supported kinds: insert_after, insert_before, replace, delete, create_file, delete_file.',
|
|
68
|
-
'Paths must stay relative to the workspace root (no leading "/" or drive letters).',
|
|
69
|
-
'Insert operations require `anchor` text; replace/delete require exact `target` snippets; `lines` omit "+/-" prefixes.',
|
|
70
|
-
'If you must emit raw patch text, use "*** Begin Patch" / "*** End Patch" with "*** Update/Add/Delete File" headers (no "diff --git").',
|
|
71
|
-
'Do not output "*** Create File:"; use "*** Add File:".'
|
|
72
|
-
].join('\n');
|
|
114
|
+
const { marker, text: guidance } = buildApplyPatchGuidanceText();
|
|
73
115
|
const params = ensureObjectSchema(fn.parameters);
|
|
74
116
|
const props = params.properties;
|
|
75
|
-
props.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
additionalProperties: false,
|
|
83
|
-
required: ['kind'],
|
|
84
|
-
properties: {
|
|
85
|
-
file: { type: 'string', description: 'Relative path for this change (overrides top-level file)' },
|
|
86
|
-
kind: {
|
|
87
|
-
type: 'string',
|
|
88
|
-
description: 'insert_after | insert_before | replace | delete | create_file | delete_file'
|
|
89
|
-
},
|
|
90
|
-
anchor: { type: 'string', description: 'Required for insert_before / insert_after' },
|
|
91
|
-
target: { type: 'string', description: 'Snippet to replace/delete' },
|
|
92
|
-
lines: {
|
|
93
|
-
description: 'New content for insert/replace/create operations',
|
|
94
|
-
oneOf: [
|
|
95
|
-
{ type: 'string' },
|
|
96
|
-
{ type: 'array', items: { type: 'string' } }
|
|
97
|
-
]
|
|
98
|
-
},
|
|
99
|
-
use_anchor_indent: {
|
|
100
|
-
type: 'boolean',
|
|
101
|
-
description: 'Reapply the indentation of the anchor snippet to inserted lines'
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
117
|
+
props.patch = {
|
|
118
|
+
type: 'string',
|
|
119
|
+
description: 'Patch text. Supports "*** Begin Patch" format or GNU unified diff. Paths must be workspace-relative.'
|
|
120
|
+
};
|
|
121
|
+
props.input = {
|
|
122
|
+
type: 'string',
|
|
123
|
+
description: 'Alias of patch (patch text). Prefer using patch.'
|
|
105
124
|
};
|
|
106
125
|
params.required = Array.isArray(params.required) ? params.required : [];
|
|
107
|
-
if (!params.required.includes('
|
|
108
|
-
params.required.push('
|
|
126
|
+
if (!params.required.includes('patch')) {
|
|
127
|
+
params.required.push('patch');
|
|
109
128
|
}
|
|
110
129
|
params.additionalProperties = false;
|
|
111
130
|
fn.parameters = params;
|
|
@@ -161,8 +180,11 @@ export function augmentOpenAITools(tools) {
|
|
|
161
180
|
try {
|
|
162
181
|
if (n === 'shell')
|
|
163
182
|
augmentShell(fn);
|
|
164
|
-
else if (n === 'apply_patch')
|
|
183
|
+
else if (n === 'apply_patch') {
|
|
184
|
+
const { marker, text } = buildApplyPatchGuidanceText();
|
|
185
|
+
fn.description = appendOnce(fn.description, text, marker);
|
|
165
186
|
augmentApplyPatch(fn);
|
|
187
|
+
}
|
|
166
188
|
else if (n === 'update_plan')
|
|
167
189
|
augmentUpdatePlan(fn);
|
|
168
190
|
else if (n === 'view_image')
|
|
@@ -194,49 +216,22 @@ export function augmentAnthropicTools(tools) {
|
|
|
194
216
|
const n = name.trim();
|
|
195
217
|
try {
|
|
196
218
|
if (n === 'apply_patch') {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
additionalProperties: false,
|
|
206
|
-
required: ['kind'],
|
|
207
|
-
properties: {
|
|
208
|
-
file: { type: 'string' },
|
|
209
|
-
kind: { type: 'string' },
|
|
210
|
-
anchor: { type: 'string' },
|
|
211
|
-
target: { type: 'string' },
|
|
212
|
-
lines: {
|
|
213
|
-
oneOf: [
|
|
214
|
-
{ type: 'string' },
|
|
215
|
-
{ type: 'array', items: { type: 'string' } }
|
|
216
|
-
]
|
|
217
|
-
},
|
|
218
|
-
use_anchor_indent: { type: 'boolean' }
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
}
|
|
219
|
+
schema.properties.patch = {
|
|
220
|
+
type: 'string',
|
|
221
|
+
description: 'Patch text (*** Begin Patch / *** End Patch or GNU unified diff).'
|
|
222
|
+
};
|
|
223
|
+
schema.properties.input = {
|
|
224
|
+
type: 'string',
|
|
225
|
+
description: 'Alias of patch (patch text). Prefer patch.'
|
|
226
|
+
};
|
|
223
227
|
if (!Array.isArray(schema.required))
|
|
224
228
|
schema.required = [];
|
|
225
|
-
if (!schema.required.includes('
|
|
226
|
-
schema.required.push('
|
|
229
|
+
if (!schema.required.includes('patch'))
|
|
230
|
+
schema.required.push('patch');
|
|
227
231
|
schema.additionalProperties = false;
|
|
228
232
|
copy.input_schema = schema;
|
|
229
233
|
const marker = '[Codex ApplyPatch Guidance]';
|
|
230
|
-
const guidance =
|
|
231
|
-
marker,
|
|
232
|
-
'Before using apply_patch, always read the latest content of the target file (via shell or another tool) and base your changes on that content.',
|
|
233
|
-
'Provide structured changes (insert_after / insert_before / replace / delete / create_file / delete_file) instead of raw patch text.',
|
|
234
|
-
'Each change must include the target file (relative path) plus anchor/target snippets and the replacement lines.',
|
|
235
|
-
'所有路径必须相对工作区根目录,禁止输出以 / 或盘符开头的绝对路径。',
|
|
236
|
-
'Example: {\"changes\":[{\"file\":\"src/app.ts\",\"kind\":\"replace\",\"target\":\"const answer = 41;\",\"lines\":[\"const answer = 42;\"]}]}(修改同一文件时尽量只修改一段连续区域,多处不相邻修改请拆成多次 apply_patch 调用).',
|
|
237
|
-
'Raw patch text must use "*** Begin Patch" / "*** End Patch" + "*** Update/Add/Delete File" headers(不要输出 "diff --git")。',
|
|
238
|
-
'不要输出 "*** Create File:";请使用 "*** Add File:".'
|
|
239
|
-
].join('\n');
|
|
234
|
+
const guidance = buildApplyPatchGuidanceText().text;
|
|
240
235
|
copy.description = appendOnce(desc, guidance, marker);
|
|
241
236
|
}
|
|
242
237
|
if (n === 'shell') {
|
|
@@ -271,7 +266,7 @@ export function buildSystemToolGuidance() {
|
|
|
271
266
|
lines.push(bullet('shell: Place ALL intent into the command argv array only; do not invent extra keys. / shell 所有意图写入 command 数组,不要添加额外键名。'));
|
|
272
267
|
lines.push(bullet('File writes are FORBIDDEN via shell (no redirection, no here-doc, no sed -i, no ed -s, no tee). Use apply_patch ONLY. / 通过 shell 写文件一律禁止(不得使用重定向、heredoc、sed -i、ed -s、tee);必须使用 apply_patch。'));
|
|
273
268
|
lines.push(bullet('apply_patch: Before writing, always read the target file first and compute changes against the latest content using appropriate tools. / apply_patch 在写入前必须先通过合适的工具读取目标文件最新内容,并基于该内容生成变更。'));
|
|
274
|
-
lines.push(bullet('apply_patch:
|
|
269
|
+
lines.push(bullet('apply_patch: Send patch text only. Supported formats: "*** Begin Patch" OR GNU unified diff. "*** Update File" never implicitly creates files—use "*** Add File:" (or /dev/null diff). / apply_patch:仅发送补丁文本,支持 "*** Begin Patch" 或 GNU diff;"*** Update File" 不会隐式创建文件。'));
|
|
275
270
|
lines.push(bullet('apply_patch: For a given file, prefer one contiguous change block per call; if you need to touch non-adjacent regions, split them into multiple apply_patch calls. / apply_patch 修改同一文件时尽量只提交一段连续补丁,多个不相邻位置请拆成多次调用。'));
|
|
276
271
|
lines.push(bullet('update_plan: Keep exactly one step in_progress; others pending/completed. / 仅一个 in_progress 步骤。'));
|
|
277
272
|
lines.push(bullet('view_image: Path must be an image file (.png .jpg .jpeg .gif .webp .bmp .svg). / 仅图片路径。'));
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ServerResponse } from 'node:http';
|
|
2
|
+
import type { Readable } from 'node:stream';
|
|
3
|
+
type AnyObj = Record<string, unknown>;
|
|
4
|
+
/**
|
|
5
|
+
* Pipe an SSE Readable to Node http.ServerResponse
|
|
6
|
+
*/
|
|
7
|
+
export declare function sendNodeSSE(res: ServerResponse, stream: Readable): void;
|
|
8
|
+
/**
|
|
9
|
+
* Express response variant (duck-typed): res.set, res.write, res.end
|
|
10
|
+
*/
|
|
11
|
+
export declare function sendExpressSSE(res: AnyObj, stream: Readable): void;
|
|
12
|
+
/**
|
|
13
|
+
* Fastify reply variant: reply.raw is ServerResponse
|
|
14
|
+
*/
|
|
15
|
+
export declare function sendFastifySSE(reply: AnyObj, stream: Readable): void;
|
|
16
|
+
/**
|
|
17
|
+
* If the given payload contains __sse_responses or sseStream, stream it via the provided response object.
|
|
18
|
+
* Otherwise, send JSON.
|
|
19
|
+
* - Supported responders: Node ServerResponse, Express res, Fastify reply
|
|
20
|
+
*/
|
|
21
|
+
export declare function sendSSEOrJSON(resOrReply: AnyObj, payload: AnyObj): any;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type VirtualRouterBootstrapInput, type VirtualRouterBootstrapResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* 将用户提供的 Virtual Router 配置(或包含 virtualrouter 字段的整体配置)
|
|
4
|
+
* 规范化为 VirtualRouterConfig,供 HubPipeline / VirtualRouterEngine 直接使用。
|
|
5
|
+
*/
|
|
6
|
+
export declare function bootstrapVirtualRouterConfig(input: VirtualRouterBootstrapInput): VirtualRouterBootstrapResult;
|
|
@@ -29,12 +29,32 @@ export function bootstrapVirtualRouterConfig(input) {
|
|
|
29
29
|
}
|
|
30
30
|
const webSearch = normalizeWebSearch(section.webSearch, routingSource);
|
|
31
31
|
validateWebSearchRouting(webSearch, routingSource);
|
|
32
|
-
const
|
|
32
|
+
const execCommandGuard = normalizeExecCommandGuard(section.execCommandGuard);
|
|
33
|
+
const { runtimeEntries, aliasIndex, modelIndex } = buildProviderRuntimeEntries(providersSource);
|
|
33
34
|
const { routing, targetKeys } = expandRoutingTable(routingSource, aliasIndex);
|
|
34
35
|
if (!routing.default || routing.default.length === 0) {
|
|
35
36
|
throw new VirtualRouterError('Virtual Router default route must contain at least one provider target', VirtualRouterErrorCode.CONFIG_ERROR);
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
+
// Build provider profiles for:
|
|
39
|
+
// - routing targets (targetKeys), and
|
|
40
|
+
// - all declared provider models across all auth aliases (modelIndex × aliasIndex),
|
|
41
|
+
// so that "direct model" and "prefer model" routing can bypass route pools when needed.
|
|
42
|
+
const expandedTargetKeys = new Set(targetKeys);
|
|
43
|
+
for (const [providerId, aliases] of aliasIndex.entries()) {
|
|
44
|
+
const models = modelIndex.get(providerId) ?? [];
|
|
45
|
+
if (!models.length || !aliases.length) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
for (const alias of aliases) {
|
|
49
|
+
const runtimeKey = buildRuntimeKey(providerId, alias);
|
|
50
|
+
for (const modelId of models) {
|
|
51
|
+
if (modelId && typeof modelId === 'string') {
|
|
52
|
+
expandedTargetKeys.add(`${runtimeKey}.${modelId}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const { profiles: providerProfiles, targetRuntime } = buildProviderProfiles(expandedTargetKeys, runtimeEntries);
|
|
38
58
|
const classifier = normalizeClassifier(section.classifier);
|
|
39
59
|
const loadBalancing = section.loadBalancing ?? DEFAULT_LOAD_BALANCING;
|
|
40
60
|
const health = section.health ?? DEFAULT_HEALTH;
|
|
@@ -46,7 +66,8 @@ export function bootstrapVirtualRouterConfig(input) {
|
|
|
46
66
|
loadBalancing,
|
|
47
67
|
health,
|
|
48
68
|
contextRouting,
|
|
49
|
-
...(webSearch ? { webSearch } : {})
|
|
69
|
+
...(webSearch ? { webSearch } : {}),
|
|
70
|
+
...(execCommandGuard ? { execCommandGuard } : {})
|
|
50
71
|
};
|
|
51
72
|
return {
|
|
52
73
|
config,
|
|
@@ -68,13 +89,17 @@ function extractVirtualRouterSection(input) {
|
|
|
68
89
|
const health = normalizeHealth(section.health ?? root.health);
|
|
69
90
|
const contextRouting = normalizeContextRouting(section.contextRouting ?? root.contextRouting);
|
|
70
91
|
const webSearch = section.webSearch ?? root.webSearch;
|
|
71
|
-
|
|
92
|
+
const execCommandGuard = section.execCommandGuard ?? root.execCommandGuard;
|
|
93
|
+
return { providers, routing, classifier, loadBalancing, health, contextRouting, webSearch, execCommandGuard };
|
|
72
94
|
}
|
|
73
95
|
function buildProviderRuntimeEntries(providers) {
|
|
74
96
|
const runtimeEntries = {};
|
|
75
97
|
const aliasIndex = new Map();
|
|
98
|
+
const modelIndex = new Map();
|
|
76
99
|
for (const [providerId, providerRaw] of Object.entries(providers)) {
|
|
77
100
|
const normalizedProvider = normalizeProvider(providerId, providerRaw);
|
|
101
|
+
const modelsNode = asRecord(providerRaw?.models);
|
|
102
|
+
modelIndex.set(providerId, modelsNode ? Object.keys(modelsNode).filter(Boolean) : []);
|
|
78
103
|
const authEntries = extractProviderAuthEntries(providerId, providerRaw);
|
|
79
104
|
if (!authEntries.length) {
|
|
80
105
|
throw new VirtualRouterError(`Provider ${providerId} requires at least one auth entry`, VirtualRouterErrorCode.CONFIG_ERROR);
|
|
@@ -126,7 +151,7 @@ function buildProviderRuntimeEntries(providers) {
|
|
|
126
151
|
};
|
|
127
152
|
}
|
|
128
153
|
}
|
|
129
|
-
return { runtimeEntries, aliasIndex };
|
|
154
|
+
return { runtimeEntries, aliasIndex, modelIndex };
|
|
130
155
|
}
|
|
131
156
|
function expandRoutingTable(routingSource, aliasIndex) {
|
|
132
157
|
const routing = {};
|
|
@@ -690,6 +715,25 @@ function normalizeWebSearch(input, routingSource) {
|
|
|
690
715
|
...(force ? { force } : {})
|
|
691
716
|
};
|
|
692
717
|
}
|
|
718
|
+
function normalizeExecCommandGuard(input) {
|
|
719
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
720
|
+
return undefined;
|
|
721
|
+
}
|
|
722
|
+
const record = input;
|
|
723
|
+
const enabledRaw = record.enabled;
|
|
724
|
+
const enabled = enabledRaw === true ||
|
|
725
|
+
(typeof enabledRaw === 'string' && enabledRaw.trim().toLowerCase() === 'true') ||
|
|
726
|
+
(typeof enabledRaw === 'number' && enabledRaw === 1);
|
|
727
|
+
if (!enabled) {
|
|
728
|
+
return undefined;
|
|
729
|
+
}
|
|
730
|
+
const policyFileRaw = record.policyFile ?? record?.policy_file;
|
|
731
|
+
const policyFile = typeof policyFileRaw === 'string' && policyFileRaw.trim().length ? policyFileRaw.trim() : undefined;
|
|
732
|
+
return {
|
|
733
|
+
enabled: true,
|
|
734
|
+
...(policyFile ? { policyFile } : {})
|
|
735
|
+
};
|
|
736
|
+
}
|
|
693
737
|
function extractProviderAuthEntries(providerId, raw) {
|
|
694
738
|
const provider = asRecord(raw);
|
|
695
739
|
const auth = asRecord(provider.auth);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ClassificationResult, RoutingFeatures, VirtualRouterClassifierConfig } from './types.js';
|
|
2
|
+
export declare class RoutingClassifier {
|
|
3
|
+
private readonly config;
|
|
4
|
+
constructor(config: VirtualRouterClassifierConfig);
|
|
5
|
+
classify(features: RoutingFeatures): ClassificationResult;
|
|
6
|
+
private buildResult;
|
|
7
|
+
private ensureDefaultCandidate;
|
|
8
|
+
private orderRoutes;
|
|
9
|
+
private routeWeight;
|
|
10
|
+
}
|