@librechat/agents 3.3.5 → 3.3.6
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/cjs/hooks/createWorkspacePolicyHook.cjs +1 -1
- package/dist/cjs/llm/openai/index.cjs +51 -6
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +14 -0
- package/dist/cjs/session/JsonlSessionStore.cjs +1 -1
- package/dist/cjs/stream.cjs +6 -1
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +3 -1
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +2 -0
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +19 -16
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +2 -0
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ReadFile.cjs +8 -4
- package/dist/cjs/tools/ReadFile.cjs.map +1 -1
- package/dist/cjs/tools/SkillTool.cjs +2 -0
- package/dist/cjs/tools/SkillTool.cjs.map +1 -1
- package/dist/cjs/tools/SubagentTool.cjs +3 -0
- package/dist/cjs/tools/SubagentTool.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +88 -27
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +5 -1
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
- package/dist/cjs/tools/intentArg.cjs +230 -0
- package/dist/cjs/tools/intentArg.cjs.map +1 -0
- package/dist/cjs/tools/local/CompileCheckTool.cjs +3 -2
- package/dist/cjs/tools/local/CompileCheckTool.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalCodingTools.cjs +13 -12
- package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalExecutionEngine.cjs +2 -2
- package/dist/cjs/tools/ptcTimeout.cjs.map +1 -1
- package/dist/cjs/tools/search/schema.cjs +2 -0
- package/dist/cjs/tools/search/schema.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +31 -1
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +1 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/tools/toolOutputReferences.cjs +41 -7
- package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
- package/dist/esm/hooks/createWorkspacePolicyHook.mjs +1 -1
- package/dist/esm/llm/openai/index.mjs +51 -6
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/main.mjs +4 -3
- package/dist/esm/session/JsonlSessionStore.mjs +1 -1
- package/dist/esm/stream.mjs +6 -1
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +3 -1
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +2 -0
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +19 -16
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +2 -0
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ReadFile.mjs +8 -4
- package/dist/esm/tools/ReadFile.mjs.map +1 -1
- package/dist/esm/tools/SkillTool.mjs +2 -0
- package/dist/esm/tools/SkillTool.mjs.map +1 -1
- package/dist/esm/tools/SubagentTool.mjs +3 -0
- package/dist/esm/tools/SubagentTool.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +88 -28
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs +5 -1
- package/dist/esm/tools/ToolSearch.mjs.map +1 -1
- package/dist/esm/tools/intentArg.mjs +220 -0
- package/dist/esm/tools/intentArg.mjs.map +1 -0
- package/dist/esm/tools/local/CompileCheckTool.mjs +3 -2
- package/dist/esm/tools/local/CompileCheckTool.mjs.map +1 -1
- package/dist/esm/tools/local/LocalCodingTools.mjs +13 -12
- package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -1
- package/dist/esm/tools/local/LocalExecutionEngine.mjs +2 -2
- package/dist/esm/tools/ptcTimeout.mjs.map +1 -1
- package/dist/esm/tools/search/schema.mjs +2 -0
- package/dist/esm/tools/search/schema.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +31 -2
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +1 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/tools/toolOutputReferences.mjs +41 -7
- package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/llm/openai/index.d.ts +0 -11
- package/dist/types/tools/BashExecutor.d.ts +18 -0
- package/dist/types/tools/CodeExecutor.d.ts +18 -0
- package/dist/types/tools/ReadFile.d.ts +18 -0
- package/dist/types/tools/SkillTool.d.ts +18 -0
- package/dist/types/tools/SubagentTool.d.ts +9 -0
- package/dist/types/tools/ToolNode.d.ts +37 -5
- package/dist/types/tools/ToolSearch.d.ts +18 -0
- package/dist/types/tools/intentArg.d.ts +119 -0
- package/dist/types/tools/ptcTimeout.d.ts +2 -0
- package/dist/types/tools/search/schema.d.ts +18 -0
- package/dist/types/tools/search/tool.d.ts +18 -0
- package/dist/types/tools/toolOutputReferences.d.ts +12 -2
- package/dist/types/types/stream.d.ts +12 -0
- package/dist/types/types/tools.d.ts +27 -2
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/llm/openai/index.ts +74 -10
- package/src/llm/openai/llm.spec.ts +105 -0
- package/src/stream.ts +12 -1
- package/src/tools/BashExecutor.ts +6 -0
- package/src/tools/BashProgrammaticToolCalling.ts +2 -0
- package/src/tools/CodeExecutor.ts +6 -0
- package/src/tools/ProgrammaticToolCalling.ts +2 -0
- package/src/tools/ReadFile.ts +2 -0
- package/src/tools/SkillTool.ts +2 -0
- package/src/tools/SubagentTool.ts +3 -0
- package/src/tools/ToolNode.ts +190 -43
- package/src/tools/ToolSearch.ts +3 -0
- package/src/tools/__tests__/ToolNode.onResultCompletion.test.ts +174 -0
- package/src/tools/__tests__/intentArg.test.ts +281 -0
- package/src/tools/__tests__/intentCoverage.test.ts +139 -0
- package/src/tools/__tests__/toolOutputReferences.test.ts +83 -0
- package/src/tools/intentArg.ts +323 -0
- package/src/tools/local/CompileCheckTool.ts +4 -3
- package/src/tools/local/LocalCodingTools.ts +14 -13
- package/src/tools/ptcTimeout.ts +2 -0
- package/src/tools/search/outcome.test.ts +90 -0
- package/src/tools/search/schema.ts +3 -0
- package/src/tools/search/tool.ts +47 -1
- package/src/tools/subagent/SubagentExecutor.ts +2 -1
- package/src/tools/toolOutputReferences.ts +77 -9
- package/src/types/stream.ts +12 -0
- package/src/types/tools.ts +24 -2
|
@@ -36,6 +36,15 @@ export declare const newsSchema: {
|
|
|
36
36
|
export declare const WebSearchToolSchema: {
|
|
37
37
|
readonly type: "object";
|
|
38
38
|
readonly properties: {
|
|
39
|
+
readonly intent: {
|
|
40
|
+
readonly type: "string" | "number" | "integer" | "float" | "boolean" | "array" | "object";
|
|
41
|
+
readonly enum?: string[];
|
|
42
|
+
readonly items?: import("../..").JsonSchemaType;
|
|
43
|
+
readonly properties?: Record<string, import("../..").JsonSchemaType>;
|
|
44
|
+
readonly required?: string[];
|
|
45
|
+
readonly description?: string;
|
|
46
|
+
readonly additionalProperties?: boolean | import("../..").JsonSchemaType;
|
|
47
|
+
};
|
|
39
48
|
readonly query: {
|
|
40
49
|
readonly type: "string";
|
|
41
50
|
readonly description: string;
|
|
@@ -72,6 +81,15 @@ export declare const WebSearchToolDefinition: {
|
|
|
72
81
|
readonly schema: {
|
|
73
82
|
readonly type: "object";
|
|
74
83
|
readonly properties: {
|
|
84
|
+
readonly intent: {
|
|
85
|
+
readonly type: "string" | "number" | "integer" | "float" | "boolean" | "array" | "object";
|
|
86
|
+
readonly enum?: string[];
|
|
87
|
+
readonly items?: import("../..").JsonSchemaType;
|
|
88
|
+
readonly properties?: Record<string, import("../..").JsonSchemaType>;
|
|
89
|
+
readonly required?: string[];
|
|
90
|
+
readonly description?: string;
|
|
91
|
+
readonly additionalProperties?: boolean | import("../..").JsonSchemaType;
|
|
92
|
+
};
|
|
75
93
|
readonly query: {
|
|
76
94
|
readonly type: "string";
|
|
77
95
|
readonly description: string;
|
|
@@ -2,6 +2,24 @@ import { DynamicStructuredTool } from '@langchain/core/tools';
|
|
|
2
2
|
import type * as t from './types';
|
|
3
3
|
import { DATE_RANGE } from './schema';
|
|
4
4
|
import { createSearchAPI } from './search';
|
|
5
|
+
/**
|
|
6
|
+
* Settled label for a `web_search` call's intent (see `intentArg.ts`).
|
|
7
|
+
*
|
|
8
|
+
* Counts the result kinds `formatResultsForLLM` actually renders —
|
|
9
|
+
* `references` only tracks links embedded in extracted highlights, so it
|
|
10
|
+
* undercounts ordinary results and can overcount when one highlight embeds
|
|
11
|
+
* several links.
|
|
12
|
+
*
|
|
13
|
+
* A caught provider or processing failure is reported through `data.error`
|
|
14
|
+
* while the tool still returns NORMALLY, so that case must author its own
|
|
15
|
+
* label: the `ToolMessage` carries success status, and a bare intent would
|
|
16
|
+
* otherwise settle mechanically from "Searching…" to "Searched…" and present
|
|
17
|
+
* a failed search as a successful one.
|
|
18
|
+
*
|
|
19
|
+
* Returns undefined for a genuine zero-result search, leaving the host's
|
|
20
|
+
* mechanical past-tense transform to label it.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveSearchOutcome(data: t.SearchResultData, query: string): string | undefined;
|
|
5
23
|
/**
|
|
6
24
|
* Executes parallel searches and merges the results,
|
|
7
25
|
* deduplicating top stories by link
|
|
@@ -69,8 +69,18 @@ export type ResolveResult<T> = {
|
|
|
69
69
|
* point in time, ignoring any subsequent registrations.
|
|
70
70
|
*/
|
|
71
71
|
export interface ToolOutputResolveView {
|
|
72
|
-
resolve<T>(args: T): ResolveResult<T>;
|
|
72
|
+
resolve<T>(args: T, options?: ResolveOptions): ResolveResult<T>;
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Per-call resolution options. `substituteIntentKey` opts the top-level
|
|
76
|
+
* `intent` key back INTO placeholder substitution: the exemption protects
|
|
77
|
+
* the injected display label, but a tool whose own schema declares a
|
|
78
|
+
* business parameter named `intent` (the injectors skip such tools) still
|
|
79
|
+
* needs references piped into it like any other argument.
|
|
80
|
+
*/
|
|
81
|
+
export type ResolveOptions = {
|
|
82
|
+
substituteIntentKey?: boolean;
|
|
83
|
+
};
|
|
74
84
|
/**
|
|
75
85
|
* Pre-resolved arg map keyed by `toolCallId`. Used by the mixed
|
|
76
86
|
* direct+event dispatch path to feed event calls' resolved args
|
|
@@ -166,7 +176,7 @@ export declare class ToolOutputReferenceRegistry {
|
|
|
166
176
|
* the serialized args, the original input is returned without
|
|
167
177
|
* walking the tree.
|
|
168
178
|
*/
|
|
169
|
-
resolve<T>(runId: string | undefined, args: T): ResolveResult<T>;
|
|
179
|
+
resolve<T>(runId: string | undefined, args: T, options?: ResolveOptions): ResolveResult<T>;
|
|
170
180
|
/**
|
|
171
181
|
* Captures a frozen snapshot of `runId`'s current entries and
|
|
172
182
|
* returns a view that resolves placeholders against *only* that
|
|
@@ -98,6 +98,13 @@ export type ProcessedToolCall = {
|
|
|
98
98
|
id: string;
|
|
99
99
|
output: string;
|
|
100
100
|
progress: number;
|
|
101
|
+
/**
|
|
102
|
+
* Settled label for the call, resolved from the tool-supplied
|
|
103
|
+
* `outcome`/`outcome_patch` result fields against the model-authored
|
|
104
|
+
* `intent` arg. Only present when the tool authored one — hosts apply
|
|
105
|
+
* the mechanical intent transform themselves when absent.
|
|
106
|
+
*/
|
|
107
|
+
outcome?: string;
|
|
101
108
|
};
|
|
102
109
|
export type ProcessedContent = {
|
|
103
110
|
type: ContentType;
|
|
@@ -265,6 +272,11 @@ export type ToolCallPart = {
|
|
|
265
272
|
id?: string;
|
|
266
273
|
/** If provided, the output of the tool call */
|
|
267
274
|
output?: ToolResultContent['content'];
|
|
275
|
+
/**
|
|
276
|
+
* Tool-authored settled label for the call (see `ProcessedToolCall.outcome`),
|
|
277
|
+
* preserved through aggregation so it survives persistence/reload.
|
|
278
|
+
*/
|
|
279
|
+
outcome?: string;
|
|
268
280
|
/** Auth URL */
|
|
269
281
|
auth?: string;
|
|
270
282
|
/** Expiration time */
|
|
@@ -224,8 +224,16 @@ export type ToolNodeConstructorParams = ToolRefs & ToolNodeOptions;
|
|
|
224
224
|
export type ToolEndEvent = {
|
|
225
225
|
/** The Step Id of the Tool Call */
|
|
226
226
|
id: string;
|
|
227
|
-
/**
|
|
228
|
-
|
|
227
|
+
/**
|
|
228
|
+
* The Completed Tool Call. Carries the tool-authored `outcome` label when
|
|
229
|
+
* present (see `ProcessedToolCall.outcome`) so `ON_RUN_STEP_COMPLETED`
|
|
230
|
+
* consumers can read it without an unsafe cast.
|
|
231
|
+
*/
|
|
232
|
+
tool_call: ToolCall & {
|
|
233
|
+
output?: string;
|
|
234
|
+
progress?: number;
|
|
235
|
+
outcome?: string;
|
|
236
|
+
};
|
|
229
237
|
/** The content index of the tool call */
|
|
230
238
|
index: number;
|
|
231
239
|
type?: 'tool_call';
|
|
@@ -486,6 +494,16 @@ export type InjectedMessage = {
|
|
|
486
494
|
/** Only set when source is 'skill', for compaction preservation */
|
|
487
495
|
skillName?: string;
|
|
488
496
|
};
|
|
497
|
+
/**
|
|
498
|
+
* In-place edit of a call's model-authored `intent` label: the first
|
|
499
|
+
* occurrence of `from` in the intent is replaced with `to` (case-sensitive).
|
|
500
|
+
* Lets a tool settle the label while preserving the model's own phrasing,
|
|
501
|
+
* e.g. `{ from: 'Searching', to: 'Searched' }`.
|
|
502
|
+
*/
|
|
503
|
+
export type OutcomePatch = {
|
|
504
|
+
from: string;
|
|
505
|
+
to: string;
|
|
506
|
+
};
|
|
489
507
|
/** Result for a single tool call in event-driven execution */
|
|
490
508
|
export type ToolExecuteResult = {
|
|
491
509
|
/** Matches ToolCallRequest.id */
|
|
@@ -498,6 +516,13 @@ export type ToolExecuteResult = {
|
|
|
498
516
|
status: 'success' | 'error';
|
|
499
517
|
/** Error message if status is 'error' */
|
|
500
518
|
errorMessage?: string;
|
|
519
|
+
/**
|
|
520
|
+
* Settled human-readable label for this call, replacing the model-authored
|
|
521
|
+
* `intent` arg in the UI. Full replacement; wins over `outcome_patch`.
|
|
522
|
+
*/
|
|
523
|
+
outcome?: string;
|
|
524
|
+
/** In-place edit of the model-authored `intent` label (see {@link OutcomePatch}). */
|
|
525
|
+
outcome_patch?: OutcomePatch;
|
|
501
526
|
/**
|
|
502
527
|
* Messages to inject into graph state after the ToolMessage for this call.
|
|
503
528
|
* Placed after tool results to respect provider message ordering (tool_call -> tool_result adjacency).
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -26,6 +26,7 @@ export * from './tools/ReadFile';
|
|
|
26
26
|
export * from './tools/skillCatalog';
|
|
27
27
|
export * from './tools/ToolSearch';
|
|
28
28
|
export * from './tools/ToolNode';
|
|
29
|
+
export * from './tools/intentArg';
|
|
29
30
|
export * from './tools/schema';
|
|
30
31
|
export * from './tools/handlers';
|
|
31
32
|
export * from './tools/local';
|
package/src/llm/openai/index.ts
CHANGED
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
projectOpenAIResponsesToolMessageContent,
|
|
52
52
|
projectToolStreamContentForProvider,
|
|
53
53
|
} from '@/messages/core';
|
|
54
|
+
import { INTENT_ARG, isIntentLabelProperty } from '@/tools/intentArg';
|
|
54
55
|
import { isReasoningModel, _convertMessagesToOpenAIParams } from './utils';
|
|
55
56
|
import { dropRepeatedScalarMetadata } from './streamMetadata';
|
|
56
57
|
|
|
@@ -1003,6 +1004,65 @@ function createAbortHandler(controller: AbortController): () => void {
|
|
|
1003
1004
|
* @param {Object} [fields] Additional fields to add to the OpenAI tool.
|
|
1004
1005
|
* @returns {ToolDefinition} The inputted tool in OpenAI tool format.
|
|
1005
1006
|
*/
|
|
1007
|
+
/**
|
|
1008
|
+
* OpenAI strict function schemas require every property to appear in
|
|
1009
|
+
* `required`. The optional `intent` label (see `tools/intentArg.ts`) is
|
|
1010
|
+
* deliberately NOT required — the same schema is callable from programmatic
|
|
1011
|
+
* tool calling — so a tool auto-marked `strict: true` (the non-streaming
|
|
1012
|
+
* `json_schema` structured-output path) would be rejected as invalid before
|
|
1013
|
+
* execution. That path never streams a live label anyway, so the
|
|
1014
|
+
* marker-identified property is dropped there; every other path keeps it.
|
|
1015
|
+
*/
|
|
1016
|
+
function stripIntentFromStrictTools<T extends object>(params: T): T {
|
|
1017
|
+
const record = params as { tools?: unknown[] };
|
|
1018
|
+
const tools = record.tools;
|
|
1019
|
+
if (!Array.isArray(tools) || tools.length === 0) {
|
|
1020
|
+
return params;
|
|
1021
|
+
}
|
|
1022
|
+
const nextTools = tools.map((tool) => {
|
|
1023
|
+
const candidate = tool as {
|
|
1024
|
+
strict?: boolean;
|
|
1025
|
+
parameters?: { properties?: Record<string, unknown>; required?: unknown };
|
|
1026
|
+
function?: {
|
|
1027
|
+
strict?: boolean;
|
|
1028
|
+
parameters?: {
|
|
1029
|
+
properties?: Record<string, unknown>;
|
|
1030
|
+
required?: unknown;
|
|
1031
|
+
};
|
|
1032
|
+
};
|
|
1033
|
+
};
|
|
1034
|
+
/** Chat-completions tools nest under `function`; responses-API tools are flat. */
|
|
1035
|
+
const holder = candidate.function ?? candidate;
|
|
1036
|
+
if (holder.strict !== true) {
|
|
1037
|
+
return tool;
|
|
1038
|
+
}
|
|
1039
|
+
const parameters = holder.parameters;
|
|
1040
|
+
const properties = parameters?.properties;
|
|
1041
|
+
if (properties == null || !isIntentLabelProperty(properties[INTENT_ARG])) {
|
|
1042
|
+
return tool;
|
|
1043
|
+
}
|
|
1044
|
+
const required = Array.isArray(parameters?.required)
|
|
1045
|
+
? (parameters.required as unknown[])
|
|
1046
|
+
: [];
|
|
1047
|
+
if (required.includes(INTENT_ARG)) {
|
|
1048
|
+
return tool;
|
|
1049
|
+
}
|
|
1050
|
+
const { [INTENT_ARG]: _omit, ...restProps } = properties;
|
|
1051
|
+
const nextParams = { ...parameters, properties: restProps };
|
|
1052
|
+
if (candidate.function != null) {
|
|
1053
|
+
return {
|
|
1054
|
+
...candidate,
|
|
1055
|
+
function: { ...candidate.function, parameters: nextParams },
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
return { ...candidate, parameters: nextParams };
|
|
1059
|
+
});
|
|
1060
|
+
if (nextTools.every((tool, index) => tool === tools[index])) {
|
|
1061
|
+
return params;
|
|
1062
|
+
}
|
|
1063
|
+
return { ...params, tools: nextTools } as T;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1006
1066
|
export function _convertToOpenAITool(
|
|
1007
1067
|
tool: BindToolsInput,
|
|
1008
1068
|
fields?: {
|
|
@@ -1184,10 +1244,12 @@ class LibreChatOpenAICompletions extends OriginalChatOpenAICompletions {
|
|
|
1184
1244
|
options?: this['ParsedCallOptions'],
|
|
1185
1245
|
extra?: { streaming?: boolean }
|
|
1186
1246
|
): ReturnType<OriginalChatOpenAICompletions['invocationParams']> {
|
|
1187
|
-
return
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1247
|
+
return stripIntentFromStrictTools(
|
|
1248
|
+
applyManagedRequestParams(super.invocationParams(options, extra), {
|
|
1249
|
+
promptCacheExplicit: this.promptCacheExplicit,
|
|
1250
|
+
safetyIdentifier: this.safetyIdentifier,
|
|
1251
|
+
})
|
|
1252
|
+
);
|
|
1191
1253
|
}
|
|
1192
1254
|
|
|
1193
1255
|
protected _getReasoningParams(
|
|
@@ -1640,7 +1702,7 @@ class LibreChatOpenAIResponses extends OriginalChatOpenAIResponses {
|
|
|
1640
1702
|
]),
|
|
1641
1703
|
];
|
|
1642
1704
|
}
|
|
1643
|
-
return params;
|
|
1705
|
+
return stripIntentFromStrictTools(params);
|
|
1644
1706
|
}
|
|
1645
1707
|
|
|
1646
1708
|
async completionWithRetry(
|
|
@@ -1741,10 +1803,12 @@ class LibreChatAzureOpenAICompletions extends OriginalAzureChatOpenAICompletions
|
|
|
1741
1803
|
options?: this['ParsedCallOptions'],
|
|
1742
1804
|
extra?: { streaming?: boolean }
|
|
1743
1805
|
): ReturnType<OriginalAzureChatOpenAICompletions['invocationParams']> {
|
|
1744
|
-
return
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1806
|
+
return stripIntentFromStrictTools(
|
|
1807
|
+
applyManagedRequestParams(super.invocationParams(options, extra), {
|
|
1808
|
+
promptCacheExplicit: this.promptCacheExplicit,
|
|
1809
|
+
safetyIdentifier: this.safetyIdentifier,
|
|
1810
|
+
})
|
|
1811
|
+
);
|
|
1748
1812
|
}
|
|
1749
1813
|
|
|
1750
1814
|
protected _getReasoningParams(
|
|
@@ -1888,7 +1952,7 @@ class LibreChatAzureOpenAIResponses extends OriginalAzureChatOpenAIResponses {
|
|
|
1888
1952
|
]),
|
|
1889
1953
|
];
|
|
1890
1954
|
}
|
|
1891
|
-
return params;
|
|
1955
|
+
return stripIntentFromStrictTools(params);
|
|
1892
1956
|
}
|
|
1893
1957
|
|
|
1894
1958
|
async completionWithRetry(
|
|
@@ -1206,6 +1206,111 @@ describe('ChatOpenAICompletions strict tools for structured output', () => {
|
|
|
1206
1206
|
toolStrict({ response_format: { type: 'json_object' } })
|
|
1207
1207
|
).toBeUndefined();
|
|
1208
1208
|
});
|
|
1209
|
+
|
|
1210
|
+
describe('optional intent labels under strict mode', () => {
|
|
1211
|
+
const intentTool = {
|
|
1212
|
+
type: 'function' as const,
|
|
1213
|
+
function: {
|
|
1214
|
+
name: 'search_mcp_docs',
|
|
1215
|
+
description: 'Search docs',
|
|
1216
|
+
parameters: {
|
|
1217
|
+
type: 'object',
|
|
1218
|
+
properties: {
|
|
1219
|
+
intent: {
|
|
1220
|
+
type: 'string',
|
|
1221
|
+
description:
|
|
1222
|
+
'ALWAYS write this field FIRST, before any other argument. One short sentence…',
|
|
1223
|
+
},
|
|
1224
|
+
query: { type: 'string' },
|
|
1225
|
+
},
|
|
1226
|
+
required: ['query'],
|
|
1227
|
+
},
|
|
1228
|
+
},
|
|
1229
|
+
};
|
|
1230
|
+
|
|
1231
|
+
function toolProperties(options: Record<string, unknown>): string[] {
|
|
1232
|
+
const model = new ChatOpenAI({ model: 'gpt-4', apiKey: 'test-key' });
|
|
1233
|
+
const completions = completionsOf<InvocationParamsDelegate>(model);
|
|
1234
|
+
const params = completions.invocationParams({
|
|
1235
|
+
tools: [intentTool],
|
|
1236
|
+
...options,
|
|
1237
|
+
}) as unknown as {
|
|
1238
|
+
tools?: { function: { parameters: { properties: object } } }[];
|
|
1239
|
+
};
|
|
1240
|
+
return Object.keys(params.tools?.[0]?.function.parameters.properties ?? {});
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
it('drops the optional label when strict is auto-enabled (invalid otherwise)', () => {
|
|
1244
|
+
expect(toolProperties({ response_format: jsonSchemaResponseFormat })).toEqual(['query']);
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
it('keeps the label on non-strict requests', () => {
|
|
1248
|
+
expect(toolProperties({})).toEqual(['intent', 'query']);
|
|
1249
|
+
});
|
|
1250
|
+
|
|
1251
|
+
/**
|
|
1252
|
+
* The Responses API flattens tools to `{type, name, parameters, strict}`
|
|
1253
|
+
* and does NOT infer strict from a `json_schema` response_format the way
|
|
1254
|
+
* Completions does — an explicit `strict` (per-call option or on the tool)
|
|
1255
|
+
* is what turns it on there.
|
|
1256
|
+
*/
|
|
1257
|
+
function responsesToolProperties(
|
|
1258
|
+
options: Record<string, unknown>
|
|
1259
|
+
): string[] {
|
|
1260
|
+
const model = new ChatOpenAI({ model: 'gpt-4', apiKey: 'test-key' });
|
|
1261
|
+
const responses = (
|
|
1262
|
+
model as unknown as {
|
|
1263
|
+
responses: {
|
|
1264
|
+
invocationParams: (o: Record<string, unknown>) => {
|
|
1265
|
+
tools?: { parameters?: { properties?: object } }[];
|
|
1266
|
+
};
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
).responses;
|
|
1270
|
+
const params = responses.invocationParams({
|
|
1271
|
+
tools: [intentTool],
|
|
1272
|
+
...options,
|
|
1273
|
+
});
|
|
1274
|
+
return Object.keys(params.tools?.[0]?.parameters?.properties ?? {});
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
it('drops the label on the RESPONSES delegate under strict too', () => {
|
|
1278
|
+
expect(responsesToolProperties({ strict: true })).toEqual(['query']);
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
it('keeps the label on a non-strict RESPONSES request', () => {
|
|
1282
|
+
expect(responsesToolProperties({})).toEqual(['intent', 'query']);
|
|
1283
|
+
});
|
|
1284
|
+
|
|
1285
|
+
it('spares a business `intent` param even under strict', () => {
|
|
1286
|
+
const businessTool = {
|
|
1287
|
+
type: 'function' as const,
|
|
1288
|
+
function: {
|
|
1289
|
+
name: 'create_record',
|
|
1290
|
+
parameters: {
|
|
1291
|
+
type: 'object',
|
|
1292
|
+
properties: {
|
|
1293
|
+
intent: { type: 'string', description: 'CRM intent category' },
|
|
1294
|
+
title: { type: 'string' },
|
|
1295
|
+
},
|
|
1296
|
+
required: ['intent', 'title'],
|
|
1297
|
+
},
|
|
1298
|
+
},
|
|
1299
|
+
};
|
|
1300
|
+
const model = new ChatOpenAI({ model: 'gpt-4', apiKey: 'test-key' });
|
|
1301
|
+
const completions = completionsOf<InvocationParamsDelegate>(model);
|
|
1302
|
+
const params = completions.invocationParams({
|
|
1303
|
+
tools: [businessTool],
|
|
1304
|
+
response_format: jsonSchemaResponseFormat,
|
|
1305
|
+
}) as unknown as {
|
|
1306
|
+
tools?: { function: { parameters: { properties: object } } }[];
|
|
1307
|
+
};
|
|
1308
|
+
expect(Object.keys(params.tools?.[0]?.function.parameters.properties ?? {})).toEqual([
|
|
1309
|
+
'intent',
|
|
1310
|
+
'title',
|
|
1311
|
+
]);
|
|
1312
|
+
});
|
|
1313
|
+
});
|
|
1209
1314
|
});
|
|
1210
1315
|
|
|
1211
1316
|
describe('ChatOpenAI._streamChatModelEvents (native, fork)', () => {
|
package/src/stream.ts
CHANGED
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
calculateMaxToolResultChars,
|
|
40
40
|
truncateToolResultContent,
|
|
41
41
|
} from '@/utils/truncation';
|
|
42
|
+
import { resolveToolOutcome, outcomeFieldsFromResult } from '@/tools/intentArg';
|
|
42
43
|
import { TOOL_OUTPUT_REF_PATTERN } from '@/tools/toolOutputReferences';
|
|
43
44
|
import { safeDispatchCustomEvent } from '@/utils/events';
|
|
44
45
|
import { isGoogleLike } from '@/utils/llm';
|
|
@@ -859,6 +860,11 @@ async function dispatchEagerToolCompletions(args: {
|
|
|
859
860
|
maxToolResultChars
|
|
860
861
|
).content;
|
|
861
862
|
}
|
|
863
|
+
const outcome = resolveToolOutcome(
|
|
864
|
+
record.request.args,
|
|
865
|
+
outcomeFieldsFromResult(result),
|
|
866
|
+
{ isError: result.status === 'error' }
|
|
867
|
+
);
|
|
862
868
|
|
|
863
869
|
try {
|
|
864
870
|
const dispatched = await safeDispatchCustomEvent(
|
|
@@ -878,6 +884,7 @@ async function dispatchEagerToolCompletions(args: {
|
|
|
878
884
|
id: result.toolCallId,
|
|
879
885
|
output,
|
|
880
886
|
progress: 1,
|
|
887
|
+
...(outcome != null && { outcome }),
|
|
881
888
|
} as t.ProcessedToolCall,
|
|
882
889
|
},
|
|
883
890
|
},
|
|
@@ -2160,7 +2167,7 @@ export function createContentAggregator(): t.ContentAggregatorResult {
|
|
|
2160
2167
|
toolCallContentIndexMap.delete(existingToolCallId);
|
|
2161
2168
|
}
|
|
2162
2169
|
|
|
2163
|
-
const newToolCall: ToolCall & t.PartMetadata = {
|
|
2170
|
+
const newToolCall: ToolCall & t.PartMetadata & { outcome?: string } = {
|
|
2164
2171
|
id,
|
|
2165
2172
|
name,
|
|
2166
2173
|
args,
|
|
@@ -2180,6 +2187,10 @@ export function createContentAggregator(): t.ContentAggregatorResult {
|
|
|
2180
2187
|
if (finalUpdate) {
|
|
2181
2188
|
newToolCall.progress = 1;
|
|
2182
2189
|
newToolCall.output = contentPart.tool_call.output;
|
|
2190
|
+
const outcome = (contentPart.tool_call as t.ToolCallPart).outcome;
|
|
2191
|
+
if (outcome != null) {
|
|
2192
|
+
newToolCall.outcome = outcome;
|
|
2193
|
+
}
|
|
2183
2194
|
}
|
|
2184
2195
|
|
|
2185
2196
|
contentParts[index] = {
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
normalizeCodeApiRequestError,
|
|
17
17
|
resolveCodeApiAuthHeaders,
|
|
18
18
|
} from './CodeExecutor';
|
|
19
|
+
import { INTENT_PROPERTY } from '@/tools/intentArg';
|
|
19
20
|
import { Constants } from '@/common';
|
|
20
21
|
|
|
21
22
|
config();
|
|
@@ -26,6 +27,7 @@ const EXEC_ENDPOINT = `${baseEndpoint}/exec`;
|
|
|
26
27
|
export const BashExecutionToolSchema = {
|
|
27
28
|
type: 'object',
|
|
28
29
|
properties: {
|
|
30
|
+
intent: { ...INTENT_PROPERTY },
|
|
29
31
|
command: {
|
|
30
32
|
type: 'string',
|
|
31
33
|
description: `The bash command or script to execute.
|
|
@@ -187,16 +189,20 @@ function createBashExecutionTool(
|
|
|
187
189
|
/* Drop any model-supplied `runtime_session_hint` from the raw args: the
|
|
188
190
|
* hint must only come from ToolNode's injected `_runtime_session_hint`
|
|
189
191
|
* (below), never from the tool call itself. */
|
|
192
|
+
/* `intent` is a UI display label — never part of the wire body. */
|
|
190
193
|
const {
|
|
191
194
|
command,
|
|
195
|
+
intent: _ignoredIntent,
|
|
192
196
|
runtime_session_hint: _ignoredModelHint,
|
|
193
197
|
...rest
|
|
194
198
|
} = rawInput as {
|
|
195
199
|
command: string;
|
|
200
|
+
intent?: unknown;
|
|
196
201
|
runtime_session_hint?: unknown;
|
|
197
202
|
args?: string[];
|
|
198
203
|
};
|
|
199
204
|
void _ignoredModelHint;
|
|
205
|
+
void _ignoredIntent;
|
|
200
206
|
const { session_id, _injected_files, _runtime_session_hint } =
|
|
201
207
|
(config.toolCall ?? {}) as {
|
|
202
208
|
session_id?: string;
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
executeTools,
|
|
22
22
|
formatCompletedResponse,
|
|
23
23
|
} from './ProgrammaticToolCalling';
|
|
24
|
+
import { INTENT_PROPERTY } from '@/tools/intentArg';
|
|
24
25
|
import { Constants } from '@/common';
|
|
25
26
|
|
|
26
27
|
config();
|
|
@@ -115,6 +116,7 @@ export function createBashProgrammaticToolCallingSchema(
|
|
|
115
116
|
return {
|
|
116
117
|
type: 'object',
|
|
117
118
|
properties: {
|
|
119
|
+
intent: { ...INTENT_PROPERTY },
|
|
118
120
|
code: {
|
|
119
121
|
type: 'string',
|
|
120
122
|
minLength: 1,
|
|
@@ -5,6 +5,7 @@ import { getEnvironmentVariable } from '@langchain/core/utils/env';
|
|
|
5
5
|
import { tool, DynamicStructuredTool } from '@langchain/core/tools';
|
|
6
6
|
import type * as t from '@/types';
|
|
7
7
|
import { appendCodeSessionFileSummary } from '@/tools/CodeSessionFileSummary';
|
|
8
|
+
import { INTENT_PROPERTY } from '@/tools/intentArg';
|
|
8
9
|
import { EnvVar, Constants } from '@/common';
|
|
9
10
|
|
|
10
11
|
export {
|
|
@@ -75,6 +76,7 @@ const SUPPORTED_LANGUAGES = [
|
|
|
75
76
|
export const CodeExecutionToolSchema = {
|
|
76
77
|
type: 'object',
|
|
77
78
|
properties: {
|
|
79
|
+
intent: { ...INTENT_PROPERTY },
|
|
78
80
|
lang: {
|
|
79
81
|
type: 'string',
|
|
80
82
|
enum: SUPPORTED_LANGUAGES,
|
|
@@ -346,18 +348,22 @@ function createCodeExecutionTool(
|
|
|
346
348
|
* injected `_runtime_session_hint` (below). Spreading `...rest` into
|
|
347
349
|
* postData would otherwise let a tool call opt itself into / pick a
|
|
348
350
|
* stateful runtime even when statefulSessions is off. */
|
|
351
|
+
/* `intent` is a UI display label — never part of the wire body. */
|
|
349
352
|
const {
|
|
350
353
|
lang,
|
|
351
354
|
code,
|
|
355
|
+
intent: _ignoredIntent,
|
|
352
356
|
runtime_session_hint: _ignoredModelHint,
|
|
353
357
|
...rest
|
|
354
358
|
} = rawInput as {
|
|
355
359
|
lang: SupportedLanguage;
|
|
356
360
|
code: string;
|
|
361
|
+
intent?: unknown;
|
|
357
362
|
runtime_session_hint?: unknown;
|
|
358
363
|
args?: string[];
|
|
359
364
|
};
|
|
360
365
|
void _ignoredModelHint;
|
|
366
|
+
void _ignoredIntent;
|
|
361
367
|
/**
|
|
362
368
|
* Extract session context from config.toolCall (injected by ToolNode).
|
|
363
369
|
* - session_id: associates with the previous run.
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
createCodeApiRunTimeoutSchema,
|
|
25
25
|
resolveCodeApiRunTimeoutMs,
|
|
26
26
|
} from './ptcTimeout';
|
|
27
|
+
import { INTENT_PROPERTY } from '@/tools/intentArg';
|
|
27
28
|
import { Constants } from '@/common';
|
|
28
29
|
|
|
29
30
|
config();
|
|
@@ -89,6 +90,7 @@ export function createProgrammaticToolCallingSchema(
|
|
|
89
90
|
return {
|
|
90
91
|
type: 'object',
|
|
91
92
|
properties: {
|
|
93
|
+
intent: { ...INTENT_PROPERTY },
|
|
92
94
|
code: {
|
|
93
95
|
type: 'string',
|
|
94
96
|
minLength: 1,
|
package/src/tools/ReadFile.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/tools/ReadFile.ts
|
|
2
|
+
import { INTENT_PROPERTY } from '@/tools/intentArg';
|
|
2
3
|
import { Constants } from '@/common';
|
|
3
4
|
|
|
4
5
|
export const ReadFileToolName = Constants.READ_FILE;
|
|
@@ -22,6 +23,7 @@ CONSTRAINTS:
|
|
|
22
23
|
export const ReadFileToolSchema = {
|
|
23
24
|
type: 'object',
|
|
24
25
|
properties: {
|
|
26
|
+
intent: { ...INTENT_PROPERTY },
|
|
25
27
|
path: {
|
|
26
28
|
type: 'string',
|
|
27
29
|
description:
|
package/src/tools/SkillTool.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/tools/SkillTool.ts
|
|
2
|
+
import { INTENT_PROPERTY } from '@/tools/intentArg';
|
|
2
3
|
import { Constants } from '@/common';
|
|
3
4
|
|
|
4
5
|
export const SkillToolName = Constants.SKILL_TOOL;
|
|
@@ -26,6 +27,7 @@ CONSTRAINTS:
|
|
|
26
27
|
export const SkillToolSchema = {
|
|
27
28
|
type: 'object',
|
|
28
29
|
properties: {
|
|
30
|
+
intent: { ...INTENT_PROPERTY },
|
|
29
31
|
skillName: {
|
|
30
32
|
type: 'string',
|
|
31
33
|
description:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { JsonSchemaType, LCTool } from '@/types/tools';
|
|
2
2
|
import type { SubagentConfig } from '@/types';
|
|
3
|
+
import { INTENT_PROPERTY } from '@/tools/intentArg';
|
|
3
4
|
import { Constants } from '@/common';
|
|
4
5
|
|
|
5
6
|
export const SubagentToolName = Constants.SUBAGENT;
|
|
@@ -29,6 +30,7 @@ const SUBAGENT_TYPE_PROP_DESCRIPTION =
|
|
|
29
30
|
export const SubagentToolSchema = {
|
|
30
31
|
type: 'object',
|
|
31
32
|
properties: {
|
|
33
|
+
intent: { ...INTENT_PROPERTY },
|
|
32
34
|
description: {
|
|
33
35
|
type: 'string',
|
|
34
36
|
description: DESCRIPTION_PROP_DESCRIPTION,
|
|
@@ -67,6 +69,7 @@ export function buildSubagentToolParams(configs: SubagentConfig[]): {
|
|
|
67
69
|
schema: {
|
|
68
70
|
type: 'object',
|
|
69
71
|
properties: {
|
|
72
|
+
intent: { ...INTENT_PROPERTY },
|
|
70
73
|
description: {
|
|
71
74
|
type: 'string',
|
|
72
75
|
description: DESCRIPTION_PROP_DESCRIPTION,
|