@jsonstudio/llms 0.6.230 → 0.6.375
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/README.md +2 -0
- package/dist/conversion/codecs/gemini-openai-codec.js +9 -1
- package/dist/conversion/compat/actions/gemini-web-search.d.ts +17 -0
- package/dist/conversion/compat/actions/gemini-web-search.js +68 -0
- package/dist/conversion/compat/actions/glm-image-content.d.ts +2 -0
- package/dist/conversion/compat/actions/glm-image-content.js +83 -0
- package/dist/conversion/compat/actions/glm-vision-prompt.d.ts +11 -0
- package/dist/conversion/compat/actions/glm-vision-prompt.js +177 -0
- package/dist/conversion/compat/actions/glm-web-search.js +25 -28
- package/dist/conversion/compat/actions/universal-shape-filter.js +11 -0
- package/dist/conversion/compat/profiles/chat-gemini.json +17 -0
- package/dist/conversion/compat/profiles/chat-glm.json +190 -184
- package/dist/conversion/compat/profiles/chat-iflow.json +195 -195
- package/dist/conversion/compat/profiles/chat-lmstudio.json +43 -43
- package/dist/conversion/compat/profiles/chat-qwen.json +20 -20
- package/dist/conversion/compat/profiles/responses-c4m.json +42 -42
- package/dist/conversion/config/sample-config.json +1 -1
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +18 -0
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +6 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +28 -1
- package/dist/conversion/hub/pipeline/target-utils.js +6 -0
- package/dist/conversion/hub/process/chat-process.js +100 -18
- package/dist/conversion/hub/response/provider-response.d.ts +13 -1
- package/dist/conversion/hub/response/provider-response.js +84 -35
- package/dist/conversion/hub/response/server-side-tools.js +61 -4
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +123 -3
- package/dist/conversion/hub/semantic-mappers/responses-mapper.js +17 -1
- package/dist/conversion/hub/standardized-bridge.js +14 -0
- package/dist/conversion/responses/responses-openai-bridge.js +35 -2
- package/dist/conversion/shared/anthropic-message-utils.js +92 -3
- package/dist/conversion/shared/bridge-message-utils.js +137 -10
- package/dist/conversion/shared/responses-output-builder.js +43 -2
- package/dist/conversion/shared/tool-filter-pipeline.js +1 -0
- package/dist/router/virtual-router/bootstrap.js +44 -12
- package/dist/router/virtual-router/classifier.js +11 -17
- package/dist/router/virtual-router/engine.d.ts +9 -0
- package/dist/router/virtual-router/engine.js +160 -18
- package/dist/router/virtual-router/features.js +1 -1
- package/dist/router/virtual-router/message-utils.js +36 -24
- package/dist/router/virtual-router/provider-registry.js +2 -1
- package/dist/router/virtual-router/token-counter.js +14 -3
- package/dist/router/virtual-router/types.d.ts +45 -0
- package/dist/router/virtual-router/types.js +2 -1
- package/dist/servertool/engine.d.ts +27 -0
- package/dist/servertool/engine.js +60 -0
- package/dist/servertool/flow-types.d.ts +40 -0
- package/dist/servertool/flow-types.js +1 -0
- package/dist/servertool/handlers/vision.d.ts +1 -0
- package/dist/servertool/handlers/vision.js +194 -0
- package/dist/servertool/handlers/web-search.d.ts +1 -0
- package/dist/servertool/handlers/web-search.js +638 -0
- package/dist/servertool/orchestration-types.d.ts +33 -0
- package/dist/servertool/orchestration-types.js +1 -0
- package/dist/servertool/registry.d.ts +18 -0
- package/dist/servertool/registry.js +27 -0
- package/dist/servertool/server-side-tools.d.ts +8 -0
- package/dist/servertool/server-side-tools.js +208 -0
- package/dist/servertool/types.d.ts +88 -0
- package/dist/servertool/types.js +1 -0
- package/dist/servertool/vision-tool.d.ts +2 -0
- package/dist/servertool/vision-tool.js +185 -0
- package/dist/sse/sse-to-json/builders/response-builder.js +6 -3
- package/package.json +1 -1
|
@@ -10,6 +10,13 @@ export interface RoutePoolTier {
|
|
|
10
10
|
targets: string[];
|
|
11
11
|
priority: number;
|
|
12
12
|
backup?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Optional force flag for this route pool.
|
|
15
|
+
* Currently interpreted for:
|
|
16
|
+
* - routing.vision: force dedicated vision backend handling.
|
|
17
|
+
* - routing.web_search / routing.search: force server-side web_search flow.
|
|
18
|
+
*/
|
|
19
|
+
force?: boolean;
|
|
13
20
|
}
|
|
14
21
|
export type RoutingPools = Record<string, RoutePoolTier[]>;
|
|
15
22
|
export type StreamingPreference = 'auto' | 'always' | 'never';
|
|
@@ -42,6 +49,13 @@ export interface ProviderProfile {
|
|
|
42
49
|
responsesConfig?: ResponsesProviderConfig;
|
|
43
50
|
streaming?: StreamingPreference;
|
|
44
51
|
maxContextTokens?: number;
|
|
52
|
+
/**
|
|
53
|
+
* When true, this provider must be skipped for any request that
|
|
54
|
+
* requires server-side tool orchestration (e.g. web_search).
|
|
55
|
+
* Normal chat routing (without servertool injection) may still
|
|
56
|
+
* use this provider as usual.
|
|
57
|
+
*/
|
|
58
|
+
serverToolsDisabled?: boolean;
|
|
45
59
|
}
|
|
46
60
|
export interface ProviderRuntimeProfile {
|
|
47
61
|
runtimeKey: string;
|
|
@@ -61,6 +75,12 @@ export interface ProviderRuntimeProfile {
|
|
|
61
75
|
modelContextTokens?: Record<string, number>;
|
|
62
76
|
defaultContextTokens?: number;
|
|
63
77
|
maxContextTokens?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Provider-level flag propagated from virtualrouter.providers[*].
|
|
80
|
+
* When true, VirtualRouterEngine will skip this runtime for any
|
|
81
|
+
* request that declares serverToolRequired=true in routing metadata.
|
|
82
|
+
*/
|
|
83
|
+
serverToolsDisabled?: boolean;
|
|
64
84
|
}
|
|
65
85
|
export interface VirtualRouterClassifierConfig {
|
|
66
86
|
longContextThresholdTokens?: number;
|
|
@@ -83,10 +103,21 @@ export interface VirtualRouterWebSearchEngineConfig {
|
|
|
83
103
|
providerKey: string;
|
|
84
104
|
description?: string;
|
|
85
105
|
default?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* When true, this engine will never be used by server-side tools
|
|
108
|
+
* (e.g. web_search). It will also be omitted from injected tool
|
|
109
|
+
* schemas so main models cannot select it for servertool flows.
|
|
110
|
+
*/
|
|
111
|
+
serverToolsDisabled?: boolean;
|
|
86
112
|
}
|
|
87
113
|
export interface VirtualRouterWebSearchConfig {
|
|
88
114
|
engines: VirtualRouterWebSearchEngineConfig[];
|
|
89
115
|
injectPolicy?: 'always' | 'selective';
|
|
116
|
+
/**
|
|
117
|
+
* When true, always prefer server-side web_search orchestration
|
|
118
|
+
* over upstream builtin behaviours (e.g. OpenAI Responses builtin web_search).
|
|
119
|
+
*/
|
|
120
|
+
force?: boolean;
|
|
90
121
|
}
|
|
91
122
|
export interface VirtualRouterConfig {
|
|
92
123
|
routing: RoutingPools;
|
|
@@ -129,6 +160,13 @@ export interface RouterMetadataInput {
|
|
|
129
160
|
providerProtocol?: string;
|
|
130
161
|
stage?: 'inbound' | 'outbound' | 'response';
|
|
131
162
|
routeHint?: string;
|
|
163
|
+
/**
|
|
164
|
+
* Indicates that current routing decision is for a request which
|
|
165
|
+
* expects server-side tools orchestration (e.g. web_search).
|
|
166
|
+
* Virtual Router should skip providers that opt out via
|
|
167
|
+
* serverToolsDisabled when this flag is true.
|
|
168
|
+
*/
|
|
169
|
+
serverToolRequired?: boolean;
|
|
132
170
|
responsesResume?: {
|
|
133
171
|
previousRequestId?: string;
|
|
134
172
|
restoredFromResponseId?: string;
|
|
@@ -182,6 +220,13 @@ export interface TargetMetadata {
|
|
|
182
220
|
responsesConfig?: ResponsesProviderConfig;
|
|
183
221
|
streaming?: StreamingPreference;
|
|
184
222
|
maxContextTokens?: number;
|
|
223
|
+
/**
|
|
224
|
+
* Route-level flags propagated from the virtual router.
|
|
225
|
+
* These are derived from routing pools and webSearch config and
|
|
226
|
+
* are used by hub pipeline/process layers (web_search / vision).
|
|
227
|
+
*/
|
|
228
|
+
forceWebSearch?: boolean;
|
|
229
|
+
forceVision?: boolean;
|
|
185
230
|
}
|
|
186
231
|
export interface ResponsesProviderConfig {
|
|
187
232
|
toolCallIdStyle?: 'fc' | 'preserve';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { AdapterContext } from '../conversion/hub/types/chat-envelope.js';
|
|
2
|
+
import type { JsonObject } from '../conversion/hub/types/json.js';
|
|
3
|
+
import type { ProviderInvoker } from './types.js';
|
|
4
|
+
export interface ServerToolOrchestrationOptions {
|
|
5
|
+
chat: JsonObject;
|
|
6
|
+
adapterContext: AdapterContext;
|
|
7
|
+
requestId: string;
|
|
8
|
+
entryEndpoint: string;
|
|
9
|
+
providerProtocol: string;
|
|
10
|
+
reenterPipeline?: (options: {
|
|
11
|
+
entryEndpoint: string;
|
|
12
|
+
requestId: string;
|
|
13
|
+
body: JsonObject;
|
|
14
|
+
metadata?: JsonObject;
|
|
15
|
+
}) => Promise<{
|
|
16
|
+
body?: JsonObject;
|
|
17
|
+
__sse_responses?: unknown;
|
|
18
|
+
format?: string;
|
|
19
|
+
}>;
|
|
20
|
+
providerInvoker?: ProviderInvoker;
|
|
21
|
+
}
|
|
22
|
+
export interface ServerToolOrchestrationResult {
|
|
23
|
+
chat: JsonObject;
|
|
24
|
+
executed: boolean;
|
|
25
|
+
flowId?: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function runServerToolOrchestration(options: ServerToolOrchestrationOptions): Promise<ServerToolOrchestrationResult>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { runServerSideToolEngine } from './server-side-tools.js';
|
|
2
|
+
export async function runServerToolOrchestration(options) {
|
|
3
|
+
const engineOptions = {
|
|
4
|
+
chatResponse: options.chat,
|
|
5
|
+
adapterContext: options.adapterContext,
|
|
6
|
+
entryEndpoint: options.entryEndpoint,
|
|
7
|
+
requestId: options.requestId,
|
|
8
|
+
providerProtocol: options.providerProtocol,
|
|
9
|
+
providerInvoker: options.providerInvoker,
|
|
10
|
+
reenterPipeline: options.reenterPipeline
|
|
11
|
+
};
|
|
12
|
+
const engineResult = await runServerSideToolEngine(engineOptions);
|
|
13
|
+
if (engineResult.mode === 'passthrough' || !engineResult.execution) {
|
|
14
|
+
return {
|
|
15
|
+
chat: engineResult.finalChatResponse,
|
|
16
|
+
executed: false
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (!engineResult.execution.followup || !options.reenterPipeline) {
|
|
20
|
+
return {
|
|
21
|
+
chat: engineResult.finalChatResponse,
|
|
22
|
+
executed: true,
|
|
23
|
+
flowId: engineResult.execution.flowId
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const routeHint = resolveRouteHint(options.adapterContext, engineResult.execution.flowId);
|
|
27
|
+
const metadata = {
|
|
28
|
+
serverToolFollowup: true,
|
|
29
|
+
stream: false,
|
|
30
|
+
...(engineResult.execution.followup.metadata ?? {})
|
|
31
|
+
};
|
|
32
|
+
if (routeHint && typeof metadata.routeHint !== 'string') {
|
|
33
|
+
metadata.routeHint = routeHint;
|
|
34
|
+
}
|
|
35
|
+
const followup = await options.reenterPipeline({
|
|
36
|
+
entryEndpoint: '/v1/chat/completions',
|
|
37
|
+
requestId: `${options.requestId}${engineResult.execution.followup.requestIdSuffix}`,
|
|
38
|
+
body: engineResult.execution.followup.payload,
|
|
39
|
+
metadata
|
|
40
|
+
});
|
|
41
|
+
const followupBody = followup.body && typeof followup.body === 'object'
|
|
42
|
+
? followup.body
|
|
43
|
+
: engineResult.finalChatResponse;
|
|
44
|
+
return {
|
|
45
|
+
chat: followupBody,
|
|
46
|
+
executed: true,
|
|
47
|
+
flowId: engineResult.execution.flowId
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function resolveRouteHint(adapterContext, flowId) {
|
|
51
|
+
const rawRoute = adapterContext.routeId;
|
|
52
|
+
const routeId = typeof rawRoute === 'string' && rawRoute.trim() ? rawRoute.trim() : '';
|
|
53
|
+
if (!routeId) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
if (flowId && routeId.toLowerCase() === flowId.toLowerCase()) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
return routeId;
|
|
60
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { JsonObject } from '../conversion/hub/types/json.js';
|
|
2
|
+
import type { ServerToolOrchestrationOptions, ServerToolOrchestrationResult } from './orchestration-types.js';
|
|
3
|
+
export interface ServerToolFlowContext {
|
|
4
|
+
options: ServerToolOrchestrationOptions;
|
|
5
|
+
baseChatResponse: JsonObject;
|
|
6
|
+
capturedChatRequest?: JsonObject;
|
|
7
|
+
routeId?: string;
|
|
8
|
+
cache: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
export interface ServerToolHopResult {
|
|
11
|
+
body?: JsonObject;
|
|
12
|
+
__sse_responses?: unknown;
|
|
13
|
+
format?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ServerToolReenterCallOptions {
|
|
16
|
+
requestIdSuffix: string;
|
|
17
|
+
body: JsonObject;
|
|
18
|
+
entryEndpoint?: string;
|
|
19
|
+
metadata?: JsonObject;
|
|
20
|
+
}
|
|
21
|
+
export interface ServerToolProviderCallOptions {
|
|
22
|
+
requestIdSuffix: string;
|
|
23
|
+
providerKey: string;
|
|
24
|
+
providerProtocol: string;
|
|
25
|
+
entryEndpoint: string;
|
|
26
|
+
payload: JsonObject;
|
|
27
|
+
modelId?: string;
|
|
28
|
+
routeHint?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface ServerToolFlowHelpers {
|
|
31
|
+
makeRequestId: (suffix: string) => string;
|
|
32
|
+
callReenterHop: (options: ServerToolReenterCallOptions) => Promise<ServerToolHopResult | null>;
|
|
33
|
+
callProviderHop: (options: ServerToolProviderCallOptions) => Promise<ServerToolHopResult | null>;
|
|
34
|
+
getRouteHintForFollowup: (exclude?: string) => string | undefined;
|
|
35
|
+
}
|
|
36
|
+
export interface ServerToolFlow {
|
|
37
|
+
id: string;
|
|
38
|
+
shouldRun: (context: ServerToolFlowContext) => Promise<boolean> | boolean;
|
|
39
|
+
run: (context: ServerToolFlowContext, helpers: ServerToolFlowHelpers) => Promise<ServerToolOrchestrationResult | null>;
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { registerServerToolHandler } from '../registry.js';
|
|
2
|
+
import { cloneJson, extractTextFromChatLike } from '../server-side-tools.js';
|
|
3
|
+
const FLOW_ID = 'vision_flow';
|
|
4
|
+
const handler = async (ctx) => {
|
|
5
|
+
if (!ctx.options.reenterPipeline) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
if (!shouldRunVisionFlow(ctx)) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const captured = getCapturedRequest(ctx.adapterContext);
|
|
12
|
+
if (!captured) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const analysisPayload = buildVisionAnalysisPayload(captured);
|
|
16
|
+
if (!analysisPayload) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const visionResponse = await ctx.options.reenterPipeline({
|
|
20
|
+
entryEndpoint: '/v1/chat/completions',
|
|
21
|
+
requestId: `${ctx.options.requestId}:vision`,
|
|
22
|
+
body: analysisPayload,
|
|
23
|
+
metadata: {
|
|
24
|
+
routeHint: 'vision',
|
|
25
|
+
serverToolFollowup: true,
|
|
26
|
+
stream: false
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
const visionBody = visionResponse.body && typeof visionResponse.body === 'object'
|
|
30
|
+
? visionResponse.body
|
|
31
|
+
: null;
|
|
32
|
+
if (!visionBody) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const visionSummary = extractTextFromChatLike(visionBody);
|
|
36
|
+
if (!visionSummary) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const followupPayload = buildVisionFollowupPayload(captured, visionSummary);
|
|
40
|
+
if (!followupPayload) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const execution = {
|
|
44
|
+
flowId: FLOW_ID,
|
|
45
|
+
followup: {
|
|
46
|
+
requestIdSuffix: ':vision_followup',
|
|
47
|
+
payload: followupPayload,
|
|
48
|
+
metadata: buildFollowupMetadata(ctx.adapterContext, 'vision')
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
chatResponse: ctx.base,
|
|
53
|
+
execution
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
registerServerToolHandler('vision_auto', handler, { trigger: 'auto' });
|
|
57
|
+
function shouldRunVisionFlow(ctx) {
|
|
58
|
+
const record = ctx.adapterContext;
|
|
59
|
+
const followupFlag = record.serverToolFollowup === true || record.serverToolFollowup === 'true';
|
|
60
|
+
if (followupFlag) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return record.hasImageAttachment === true || record.hasImageAttachment === 'true';
|
|
64
|
+
}
|
|
65
|
+
function getCapturedRequest(adapterContext) {
|
|
66
|
+
if (!adapterContext || typeof adapterContext !== 'object') {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const captured = adapterContext.capturedChatRequest;
|
|
70
|
+
if (!captured || typeof captured !== 'object' || Array.isArray(captured)) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
return captured;
|
|
74
|
+
}
|
|
75
|
+
function buildVisionAnalysisPayload(source) {
|
|
76
|
+
if (!source || typeof source !== 'object') {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const payload = {};
|
|
80
|
+
if (typeof source.model === 'string' && source.model.trim()) {
|
|
81
|
+
payload.model = source.model.trim();
|
|
82
|
+
}
|
|
83
|
+
if (Array.isArray(source.messages)) {
|
|
84
|
+
payload.messages = cloneJson(source.messages);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
if (Array.isArray(source.tools) && source.tools.length) {
|
|
90
|
+
payload.tools = cloneJson(source.tools);
|
|
91
|
+
}
|
|
92
|
+
const parameters = source.parameters;
|
|
93
|
+
if (parameters && typeof parameters === 'object' && !Array.isArray(parameters)) {
|
|
94
|
+
const params = cloneJson(parameters);
|
|
95
|
+
Object.assign(payload, params);
|
|
96
|
+
}
|
|
97
|
+
return payload;
|
|
98
|
+
}
|
|
99
|
+
function buildVisionFollowupPayload(source, summary) {
|
|
100
|
+
if (!source || typeof source !== 'object') {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const payload = {};
|
|
104
|
+
if (typeof source.model === 'string' && source.model.trim()) {
|
|
105
|
+
payload.model = source.model.trim();
|
|
106
|
+
}
|
|
107
|
+
payload.messages = injectVisionSummary(source.messages, summary);
|
|
108
|
+
if (Array.isArray(source.tools) && source.tools.length) {
|
|
109
|
+
payload.tools = cloneJson(source.tools);
|
|
110
|
+
}
|
|
111
|
+
const parameters = source.parameters;
|
|
112
|
+
if (parameters && typeof parameters === 'object' && !Array.isArray(parameters)) {
|
|
113
|
+
const params = cloneJson(parameters);
|
|
114
|
+
Object.assign(payload, params);
|
|
115
|
+
}
|
|
116
|
+
return payload;
|
|
117
|
+
}
|
|
118
|
+
function injectVisionSummary(source, summary) {
|
|
119
|
+
const messages = Array.isArray(source) ? cloneJson(source) : [];
|
|
120
|
+
let injected = false;
|
|
121
|
+
for (const message of messages) {
|
|
122
|
+
if (!message || typeof message !== 'object')
|
|
123
|
+
continue;
|
|
124
|
+
const content = message.content;
|
|
125
|
+
if (!Array.isArray(content))
|
|
126
|
+
continue;
|
|
127
|
+
const nextParts = [];
|
|
128
|
+
let removed = false;
|
|
129
|
+
for (const part of content) {
|
|
130
|
+
if (part && typeof part === 'object') {
|
|
131
|
+
const typeValue = typeof part.type === 'string'
|
|
132
|
+
? part.type.toLowerCase()
|
|
133
|
+
: '';
|
|
134
|
+
if (typeValue.includes('image')) {
|
|
135
|
+
removed = true;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
nextParts.push(part);
|
|
140
|
+
}
|
|
141
|
+
if (removed) {
|
|
142
|
+
nextParts.push({
|
|
143
|
+
type: 'text',
|
|
144
|
+
text: `[Vision] ${summary}`
|
|
145
|
+
});
|
|
146
|
+
message.content = nextParts;
|
|
147
|
+
injected = true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (!injected) {
|
|
151
|
+
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
152
|
+
const msg = messages[i];
|
|
153
|
+
if (!msg || typeof msg !== 'object')
|
|
154
|
+
continue;
|
|
155
|
+
const role = typeof msg.role === 'string'
|
|
156
|
+
? msg.role.toLowerCase()
|
|
157
|
+
: '';
|
|
158
|
+
if (role !== 'user')
|
|
159
|
+
continue;
|
|
160
|
+
const content = msg.content;
|
|
161
|
+
if (Array.isArray(content)) {
|
|
162
|
+
content.push({
|
|
163
|
+
type: 'text',
|
|
164
|
+
text: `[Vision] ${summary}`
|
|
165
|
+
});
|
|
166
|
+
injected = true;
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
if (typeof content === 'string' && content.length) {
|
|
170
|
+
msg.content = `${content}\n[Vision] ${summary}`;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
msg.content = `[Vision] ${summary}`;
|
|
174
|
+
}
|
|
175
|
+
injected = true;
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (!injected) {
|
|
180
|
+
messages.push({
|
|
181
|
+
role: 'system',
|
|
182
|
+
content: `[Vision] ${summary}`
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return messages;
|
|
186
|
+
}
|
|
187
|
+
function buildFollowupMetadata(adapterContext, toolName) {
|
|
188
|
+
const ctx = adapterContext && typeof adapterContext === 'object' ? adapterContext : null;
|
|
189
|
+
const routeId = ctx && typeof ctx.routeId === 'string' && ctx.routeId.trim() ? ctx.routeId.trim() : '';
|
|
190
|
+
if (!routeId || routeId.toLowerCase() === toolName.toLowerCase()) {
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
return { routeHint: routeId };
|
|
194
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|