@stackfactor/agent-utils 1.2.16 → 1.2.19
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 +69 -1
- package/dist/cjs/agentProto.d.ts +1 -1
- package/dist/cjs/agentProto.d.ts.map +1 -1
- package/dist/cjs/agentProto.js +6 -0
- package/dist/cjs/client.d.ts +7 -0
- package/dist/cjs/client.d.ts.map +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/langChain.d.ts +67 -0
- package/dist/cjs/langChain.d.ts.map +1 -1
- package/dist/cjs/langChain.js +317 -34
- package/dist/cjs/serve.d.ts.map +1 -1
- package/dist/cjs/serve.js +104 -37
- package/dist/esm/agentProto.d.ts +1 -1
- package/dist/esm/agentProto.d.ts.map +1 -1
- package/dist/esm/agentProto.js +6 -0
- package/dist/esm/client.d.ts +7 -0
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/langChain.d.ts +67 -0
- package/dist/esm/langChain.d.ts.map +1 -1
- package/dist/esm/langChain.js +317 -34
- package/dist/esm/serve.d.ts.map +1 -1
- package/dist/esm/serve.js +104 -37
- package/package.json +2 -2
package/dist/esm/agentProto.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import * as grpc from "@grpc/grpc-js";
|
|
|
6
6
|
* zero or more `Progress` frames (driven by the agent's `onProgress`) followed
|
|
7
7
|
* by exactly one `Result` or `Error`.
|
|
8
8
|
*/
|
|
9
|
-
export declare const PROTO = "\nsyntax = \"proto3\";\npackage stackfactor.agent.v1;\n\nservice Agent {\n rpc Execute(ExecuteRequest) returns (stream Update);\n // Liveness/health probe served by serve() itself \u2014 no agent code required.\n // deep=true also exercises the StackFactor auth callback (BACKEND_URL).\n rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse);\n}\n\nmessage ExecuteRequest {\n string content_type = 1;\n string data_json = 2;\n string config_json = 3;\n string request_json = 4;\n int32 event = 5;\n}\n\nmessage HealthCheckRequest {\n // Optional JSON carrying { authToken } for the deep backend-callback check.\n string request_json = 1;\n bool deep = 2;\n // Deep check only: repo-relative path to the agent's self-check module to run\n // (e.g. \"src/check.js\"). Empty \u2192 fall back to the conventional \"src/check.js\".\n string check_code = 3;\n // Deep check only: JSON of the resolved agent config (constantsAndVars/\n // secrets) made available to the self-check as ctx.config. May be empty.\n string config_json = 4;\n}\n\n// One result row from the agent's self-check module. The agent owns only its\n// own checks; reachability/ingress/version-drift rows are synthesised backend-\n// side. severity is optional: \"warn\" marks a failing check as non-blocking;\n// empty or \"error\" is blocking.\nmessage CheckResult {\n string name = 1;\n bool ok = 2;\n string detail = 3;\n string severity = 4;\n}\n\nmessage HealthCheckResponse {\n bool ok = 1;\n bool backend_reachable = 2;\n string version = 3;\n string message = 4;\n repeated CheckResult checks = 5;\n}\n\nmessage Update {\n oneof payload {\n Progress progress = 1;\n Result result = 2;\n ErrorInfo error = 3;\n }\n}\n\nmessage Progress { int32 progress = 1; string message = 2; }\nmessage Result { string result_json = 1; }\nmessage ErrorInfo { int32 code = 1; string message = 2; }\n";
|
|
9
|
+
export declare const PROTO = "\nsyntax = \"proto3\";\npackage stackfactor.agent.v1;\n\nservice Agent {\n rpc Execute(ExecuteRequest) returns (stream Update);\n // Liveness/health probe served by serve() itself \u2014 no agent code required.\n // deep=true also exercises the StackFactor auth callback (BACKEND_URL).\n rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse);\n}\n\nmessage ExecuteRequest {\n string content_type = 1;\n string data_json = 2;\n string config_json = 3;\n string request_json = 4;\n int32 event = 5;\n // Optional repo-relative module to run INSTEAD of the agent's main (e.g.\n // \"src/webhooks/inbound-sms.js\"). Empty -> main, which is every pre-webhook\n // caller, so this stays wire-compatible with agents built before it existed.\n // Set by the webhook receiver from the integration's webHooks[].code. Follows\n // the same untrusted-path rules as HealthCheck's check_code.\n string code = 6;\n}\n\nmessage HealthCheckRequest {\n // Optional JSON carrying { authToken } for the deep backend-callback check.\n string request_json = 1;\n bool deep = 2;\n // Deep check only: repo-relative path to the agent's self-check module to run\n // (e.g. \"src/check.js\"). Empty \u2192 fall back to the conventional \"src/check.js\".\n string check_code = 3;\n // Deep check only: JSON of the resolved agent config (constantsAndVars/\n // secrets) made available to the self-check as ctx.config. May be empty.\n string config_json = 4;\n}\n\n// One result row from the agent's self-check module. The agent owns only its\n// own checks; reachability/ingress/version-drift rows are synthesised backend-\n// side. severity is optional: \"warn\" marks a failing check as non-blocking;\n// empty or \"error\" is blocking.\nmessage CheckResult {\n string name = 1;\n bool ok = 2;\n string detail = 3;\n string severity = 4;\n}\n\nmessage HealthCheckResponse {\n bool ok = 1;\n bool backend_reachable = 2;\n string version = 3;\n string message = 4;\n repeated CheckResult checks = 5;\n}\n\nmessage Update {\n oneof payload {\n Progress progress = 1;\n Result result = 2;\n ErrorInfo error = 3;\n }\n}\n\nmessage Progress { int32 progress = 1; string message = 2; }\nmessage Result { string result_json = 1; }\nmessage ErrorInfo { int32 code = 1; string message = 2; }\n";
|
|
10
10
|
/**
|
|
11
11
|
* Loads the Agent proto package. proto-loader reads from a file, so the embedded
|
|
12
12
|
* schema is written to a temp path — keeps the package self-contained across the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentProto.d.ts","sourceRoot":"","sources":["../../src/agentProto.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,eAAe,CAAC;AAMtC;;;;;;GAMG;AACH,eAAO,MAAM,KAAK,
|
|
1
|
+
{"version":3,"file":"agentProto.d.ts","sourceRoot":"","sources":["../../src/agentProto.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,eAAe,CAAC;AAMtC;;;;;;GAMG;AACH,eAAO,MAAM,KAAK,y3EAmEjB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,QAAO,GAWnC,CAAC;AAEF,oEAAoE;AACpE,eAAO,MAAM,gBAAgB,QAAO,IAAI,CAAC,iBACc,CAAC"}
|
package/dist/esm/agentProto.js
CHANGED
|
@@ -27,6 +27,12 @@ message ExecuteRequest {
|
|
|
27
27
|
string config_json = 3;
|
|
28
28
|
string request_json = 4;
|
|
29
29
|
int32 event = 5;
|
|
30
|
+
// Optional repo-relative module to run INSTEAD of the agent's main (e.g.
|
|
31
|
+
// "src/webhooks/inbound-sms.js"). Empty -> main, which is every pre-webhook
|
|
32
|
+
// caller, so this stays wire-compatible with agents built before it existed.
|
|
33
|
+
// Set by the webhook receiver from the integration's webHooks[].code. Follows
|
|
34
|
+
// the same untrusted-path rules as HealthCheck's check_code.
|
|
35
|
+
string code = 6;
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
message HealthCheckRequest {
|
package/dist/esm/client.d.ts
CHANGED
|
@@ -10,6 +10,13 @@ export interface AgentExecuteRequest {
|
|
|
10
10
|
config_json: string;
|
|
11
11
|
request_json: string;
|
|
12
12
|
event: number;
|
|
13
|
+
/**
|
|
14
|
+
* Optional repo-relative module to run instead of the agent's `main` — the
|
|
15
|
+
* webhook receiver sets it from the integration's `webHooks[].code`. Omit (or
|
|
16
|
+
* pass "") for every other caller; the agent then runs `main` exactly as
|
|
17
|
+
* before. Agents built against an older agent-utils ignore the field.
|
|
18
|
+
*/
|
|
19
|
+
code?: string;
|
|
13
20
|
}
|
|
14
21
|
export interface CallAgentOptions {
|
|
15
22
|
/**
|
package/dist/esm/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACrE,gFAAgF;IAChF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA8DD;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,GACpB,UAAU,MAAM,EAChB,SAAS,mBAAmB,EAC5B,UAAS,gBAAqB,KAC7B,OAAO,CAAC,GAAG,CA0DV,CAAC;AAEL,MAAM,WAAW,iBAAiB;IAChC,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,+DAA+D;AAC/D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,2EAA2E;IAC3E,EAAE,EAAE,OAAO,CAAC;IACZ,gFAAgF;IAChF,gBAAgB,EAAE,OAAO,CAAC;IAC1B,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAID;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GACrB,UAAU,MAAM,EAChB,UAAS,iBAAsB,KAC9B,OAAO,CAAC,WAAW,CA8ClB,CAAC"}
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACrE,gFAAgF;IAChF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA8DD;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,GACpB,UAAU,MAAM,EAChB,SAAS,mBAAmB,EAC5B,UAAS,gBAAqB,KAC7B,OAAO,CAAC,GAAG,CA0DV,CAAC;AAEL,MAAM,WAAW,iBAAiB;IAChC,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,+DAA+D;AAC/D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,2EAA2E;IAC3E,EAAE,EAAE,OAAO,CAAC;IACZ,gFAAgF;IAChF,gBAAgB,EAAE,OAAO,CAAC;IAC1B,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAID;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GACrB,UAAU,MAAM,EAChB,UAAS,iBAAsB,KAC9B,OAAO,CAAC,WAAW,CA8ClB,CAAC"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export { constants };
|
|
|
9
9
|
export { errorHandling, AppError };
|
|
10
10
|
export type { ParsedError } from "./errorHandling.js";
|
|
11
11
|
export { langChain };
|
|
12
|
-
export type { UsageTracker } from "./langChain.js";
|
|
12
|
+
export type { UsageTracker, WebSearchConfig } from "./langChain.js";
|
|
13
13
|
export { logger };
|
|
14
14
|
export { serve };
|
|
15
15
|
export { callAgent };
|
package/dist/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,aAAa,EAAE,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC;AAEnC,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,aAAa,EAAE,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC;AAEnC,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEpE,OAAO,EAAE,MAAM,EAAE,CAAC;AAElB,OAAO,EAAE,KAAK,EAAE,CAAC;AAEjB,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,GACZ,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
package/dist/esm/langChain.d.ts
CHANGED
|
@@ -9,6 +9,73 @@ export type UsageTracker = {
|
|
|
9
9
|
tokens: {
|
|
10
10
|
[tokenKey: string]: number;
|
|
11
11
|
};
|
|
12
|
+
/**
|
|
13
|
+
* Sources returned by native web search, deduplicated by URL across every
|
|
14
|
+
* call in the run. Only present once a search has actually run. Anthropic and
|
|
15
|
+
* Google both require the original sources to be cited when their output is
|
|
16
|
+
* shown to end users, so they are surfaced here rather than discarded with
|
|
17
|
+
* the rest of the non-text content blocks.
|
|
18
|
+
*/
|
|
19
|
+
webSearchSources?: {
|
|
20
|
+
url: string;
|
|
21
|
+
title?: string;
|
|
22
|
+
}[];
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Caller-facing options for the providers' native web search tools. Enable web
|
|
26
|
+
* search by setting `config.webSearch` to `true` (provider defaults) or to one
|
|
27
|
+
* of these objects. Every field is optional and is only forwarded to the
|
|
28
|
+
* providers that accept it — see `buildWebSearchTool` for the mapping.
|
|
29
|
+
*/
|
|
30
|
+
export type WebSearchConfig = {
|
|
31
|
+
/** Anthropic only: hard cap on searches per request (`max_uses`). */
|
|
32
|
+
maxUses?: number;
|
|
33
|
+
/** Anthropic (`allowed_domains`) and OpenAI (`filters.allowed_domains`). */
|
|
34
|
+
allowedDomains?: string[];
|
|
35
|
+
/** Anthropic only (`blocked_domains`); cannot be combined with `allowedDomains`. */
|
|
36
|
+
blockedDomains?: string[];
|
|
37
|
+
/** Anthropic and OpenAI: approximate location used to localize results. */
|
|
38
|
+
userLocation?: {
|
|
39
|
+
city?: string;
|
|
40
|
+
region?: string;
|
|
41
|
+
/** Two-letter ISO 3166-1 alpha-2 code, e.g. `"US"`. */
|
|
42
|
+
country?: string;
|
|
43
|
+
/** IANA timezone ID, e.g. `"America/Los_Angeles"`. */
|
|
44
|
+
timezone?: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* OpenAI only: how much of the context window search results may consume.
|
|
48
|
+
* OpenAI's default is `"medium"`; `"low"` minimizes context at some cost to
|
|
49
|
+
* answer quality, `"high"` is the expensive end.
|
|
50
|
+
*/
|
|
51
|
+
searchContextSize?: "low" | "medium" | "high";
|
|
52
|
+
/** Gemini only: RFC 3339 window the search is restricted to. */
|
|
53
|
+
timeRange?: {
|
|
54
|
+
startTime: string;
|
|
55
|
+
endTime: string;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Anthropic only: opt out of dynamic filtering by forcing the search to be
|
|
59
|
+
* called directly (`allowed_callers: ["direct"]`). Defaults to `true` on
|
|
60
|
+
* models that support it — see `supportsAnthropicDynamicFiltering`. Turning
|
|
61
|
+
* this off means every raw search result lands in the context window.
|
|
62
|
+
*/
|
|
63
|
+
dynamicFiltering?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Anthropic only: whether search result blocks consumed by dynamic filtering
|
|
66
|
+
* are echoed back in the response. Defaults to `"excluded"`, which drops them
|
|
67
|
+
* and cuts the output tokens billed for content nothing downstream reads.
|
|
68
|
+
*/
|
|
69
|
+
responseInclusion?: "full" | "excluded";
|
|
70
|
+
/**
|
|
71
|
+
* Anthropic only: pin the dated tool version instead of letting
|
|
72
|
+
* `buildWebSearchTool` pick per model. `web_search_20250305` is basic search,
|
|
73
|
+
* `web_search_20260209` adds dynamic filtering, `web_search_20260318` adds
|
|
74
|
+
* response-inclusion control. Pinning a filtering version on a model that
|
|
75
|
+
* cannot do programmatic tool calling returns a 400 unless
|
|
76
|
+
* `dynamicFiltering: false` is also set.
|
|
77
|
+
*/
|
|
78
|
+
toolVersion?: string;
|
|
12
79
|
};
|
|
13
80
|
declare const _default: {
|
|
14
81
|
checkIfAIProviderConfigured: (config: any) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"AAuGA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"AAuGA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACvC;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACtD,CAAC;AAskBF;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,oFAAoF;IACpF,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,2EAA2E;IAC3E,YAAY,CAAC,EAAE;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,uDAAuD;QACvD,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,sDAAsD;QACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC9C,gEAAgE;IAChE,SAAS,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IACxC;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;;0CAxnB2C,GAAG,KAAG,IAAI;wBAujC/C,MAAM,aACD,MAAM,gBACH,MAAM,SACb,GAAG,EAAE,kBACI,GAAG,UACX,GAAG,KACV,GAAG;sBAiCG,GAAG,UACF,MAAM,UACN,GAAG,eACC,QAAQ,GAAG,IAAI,iBACb,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;oCA0YF,MAAM,UACT,GAAG,UACH,GAAG,oBACO,GAAG,eACT,MAAM,eACN,MAAM,wBACG,OAAO,WACpB,GAAG,cACA,MAAM,UACV,GAAG,EAAE,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDAuuBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;0CAjhC8B,GAAG,KAAG,MAAM;+CAviC9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,UACd,GAAG,KACV,IAAI;mCAujCU,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AAwjCT,wBASE"}
|
package/dist/esm/langChain.js
CHANGED
|
@@ -603,6 +603,234 @@ const resolveTemperatureSetting = (modelName, config) => {
|
|
|
603
603
|
}
|
|
604
604
|
return { temperature: clamped };
|
|
605
605
|
};
|
|
606
|
+
/**
|
|
607
|
+
* Whether a Claude model can run web search through dynamic filtering, where
|
|
608
|
+
* Claude writes and runs code that filters the search results before they reach
|
|
609
|
+
* the context window instead of loading every result into it. Requires Claude
|
|
610
|
+
* 4.6 or later (the models with programmatic tool calling); on anything earlier
|
|
611
|
+
* the filtering tool versions return a 400 unless search is pinned to
|
|
612
|
+
* `allowed_callers: ["direct"]`.
|
|
613
|
+
* @param modelName - The Claude model identifier being routed
|
|
614
|
+
* @returns `true` when the model supports dynamic filtering
|
|
615
|
+
*/
|
|
616
|
+
const supportsAnthropicDynamicFiltering = (modelName) => /^claude-(opus|sonnet|haiku)-4-(?:[6-9]|\d\d)\b/.test(modelName) ||
|
|
617
|
+
/^claude-(opus|sonnet|haiku|fable|mythos)-(?:[5-9]|\d\d)\b/.test(modelName);
|
|
618
|
+
/**
|
|
619
|
+
* Normalizes `config.webSearch` into an options object, returning `null` when
|
|
620
|
+
* web search is off so callers can use it as the single enablement gate.
|
|
621
|
+
*/
|
|
622
|
+
const getWebSearchOptions = (config) => {
|
|
623
|
+
const webSearch = config?.webSearch;
|
|
624
|
+
if (!webSearch)
|
|
625
|
+
return null;
|
|
626
|
+
return webSearch === true ? {} : webSearch;
|
|
627
|
+
};
|
|
628
|
+
/**
|
|
629
|
+
* Builds the provider-native web search tool definition for a model:
|
|
630
|
+
* - `claude-` → Anthropic's `web_search` server tool, executed by the Messages
|
|
631
|
+
* API within a single request and answered with citations. Defaults to the
|
|
632
|
+
* dynamic-filtering tool version on models that support it, so search results
|
|
633
|
+
* are filtered by code before they reach the context window.
|
|
634
|
+
* - `gpt-` → OpenAI's hosted `web_search` tool (Responses API). Context spend
|
|
635
|
+
* is governed by `search_context_size` (OpenAI defaults to `medium`).
|
|
636
|
+
* - `gemini-` → Google's `googleSearch` grounding tool. Google exposes no
|
|
637
|
+
* result-filtering or context-size control; leaving `searchTypes` unset keeps
|
|
638
|
+
* grounding on text-only web results rather than image bytes.
|
|
639
|
+
* Returns `null` for providers with no native web search (DeepSeek, Kimi, GLM),
|
|
640
|
+
* warning instead of throwing so one config can be pointed at any model.
|
|
641
|
+
* @param modelName - The model identifier being routed
|
|
642
|
+
* @param options - Normalized options from `getWebSearchOptions`
|
|
643
|
+
* @returns The provider's tool definition, or `null` when unsupported
|
|
644
|
+
*/
|
|
645
|
+
const buildWebSearchTool = (modelName, options) => {
|
|
646
|
+
const { allowedDomains, blockedDomains, userLocation } = options;
|
|
647
|
+
if (modelName.startsWith("claude-")) {
|
|
648
|
+
// The API returns a 400 when both filters are present, so fail locally
|
|
649
|
+
// rather than paying for the round trip.
|
|
650
|
+
if (allowedDomains && blockedDomains) {
|
|
651
|
+
throw errorHandlingHelper.create(constants.HTTP_CODES.BAD_REQUEST, "Anthropic web search accepts allowedDomains or blockedDomains, not both.");
|
|
652
|
+
}
|
|
653
|
+
// Prefer the newest tool version the model can actually run. Basic search
|
|
654
|
+
// loads every result into the context window; from `web_search_20260209`
|
|
655
|
+
// Claude filters them with code first, and `web_search_20260318` can also
|
|
656
|
+
// keep the consumed results out of the response.
|
|
657
|
+
const canFilter = supportsAnthropicDynamicFiltering(modelName);
|
|
658
|
+
const type = options.toolVersion ||
|
|
659
|
+
(canFilter ? "web_search_20260318" : "web_search_20250305");
|
|
660
|
+
const version = Number(type.slice(-8));
|
|
661
|
+
// Filtering versions default to running search from inside code execution.
|
|
662
|
+
// Say so explicitly when it is not wanted (or not possible), which is what
|
|
663
|
+
// the API requires from models without programmatic tool calling.
|
|
664
|
+
const directOnly = version >= 20260209 && (options.dynamicFiltering === false || !canFilter);
|
|
665
|
+
return {
|
|
666
|
+
type,
|
|
667
|
+
name: "web_search",
|
|
668
|
+
...(options.maxUses ? { max_uses: options.maxUses } : {}),
|
|
669
|
+
...(allowedDomains ? { allowed_domains: allowedDomains } : {}),
|
|
670
|
+
...(blockedDomains ? { blocked_domains: blockedDomains } : {}),
|
|
671
|
+
...(userLocation
|
|
672
|
+
? { user_location: { type: "approximate", ...userLocation } }
|
|
673
|
+
: {}),
|
|
674
|
+
...(directOnly ? { allowed_callers: ["direct"] } : {}),
|
|
675
|
+
...(version >= 20260318
|
|
676
|
+
? { response_inclusion: options.responseInclusion || "excluded" }
|
|
677
|
+
: {}),
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
if (modelName.startsWith("gpt-")) {
|
|
681
|
+
return {
|
|
682
|
+
type: "web_search",
|
|
683
|
+
...(allowedDomains
|
|
684
|
+
? { filters: { allowed_domains: allowedDomains } }
|
|
685
|
+
: {}),
|
|
686
|
+
...(userLocation
|
|
687
|
+
? { user_location: { type: "approximate", ...userLocation } }
|
|
688
|
+
: {}),
|
|
689
|
+
...(options.searchContextSize
|
|
690
|
+
? { search_context_size: options.searchContextSize }
|
|
691
|
+
: {}),
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
if (modelName.startsWith("gemini-")) {
|
|
695
|
+
// `timeRangeFilter` is the only filter the Gemini API exposes;
|
|
696
|
+
// `excludeDomains` is a Vertex AI field and is rejected here, so domain
|
|
697
|
+
// filters are deliberately not mapped for Google.
|
|
698
|
+
return {
|
|
699
|
+
googleSearch: options.timeRange
|
|
700
|
+
? { timeRangeFilter: options.timeRange }
|
|
701
|
+
: {},
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
logger.log(null, logger.levels.warn, `Model "${modelName}" has no native web search tool; ignoring the configured webSearch options.`);
|
|
705
|
+
return null;
|
|
706
|
+
};
|
|
707
|
+
/**
|
|
708
|
+
* Flattens LangChain message content into plain text. Content is a string for
|
|
709
|
+
* ordinary completions, but every provider switches to an array of blocks once
|
|
710
|
+
* a server-side tool runs — Anthropic interleaves `server_tool_use` and
|
|
711
|
+
* `web_search_tool_result` blocks with the answer text, and OpenAI's Responses
|
|
712
|
+
* API returns annotated text blocks — so without this the JSON parse pipeline
|
|
713
|
+
* would receive a non-string and every web-search call would fail.
|
|
714
|
+
* @param content - A message's `content` field, or a raw string
|
|
715
|
+
* @returns The concatenated text of all text blocks
|
|
716
|
+
*/
|
|
717
|
+
const extractTextContent = (content) => {
|
|
718
|
+
if (typeof content === "string")
|
|
719
|
+
return content;
|
|
720
|
+
if (!Array.isArray(content))
|
|
721
|
+
return "";
|
|
722
|
+
let text = "";
|
|
723
|
+
for (const block of content) {
|
|
724
|
+
if (typeof block === "string")
|
|
725
|
+
text += block;
|
|
726
|
+
else if (block?.type === "text" && typeof block.text === "string") {
|
|
727
|
+
text += block.text;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
return text;
|
|
731
|
+
};
|
|
732
|
+
const createWebSearchUsage = () => ({
|
|
733
|
+
reportedRequests: 0,
|
|
734
|
+
callIds: new Set(),
|
|
735
|
+
grounded: false,
|
|
736
|
+
sources: new Map(),
|
|
737
|
+
});
|
|
738
|
+
/**
|
|
739
|
+
* Folds one message — or one streaming chunk — into a `WebSearchUsage`. Safe to
|
|
740
|
+
* call on every chunk of a stream and on messages that involved no search.
|
|
741
|
+
*/
|
|
742
|
+
const collectWebSearchUsage = (payload, usage) => {
|
|
743
|
+
if (!payload)
|
|
744
|
+
return;
|
|
745
|
+
const addSource = (url, title) => {
|
|
746
|
+
if (typeof url === "string" && url && !usage.sources.has(url)) {
|
|
747
|
+
usage.sources.set(url, {
|
|
748
|
+
url,
|
|
749
|
+
...(typeof title === "string" ? { title } : {}),
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
if (Array.isArray(payload.content)) {
|
|
754
|
+
for (const block of payload.content) {
|
|
755
|
+
if (!block || typeof block !== "object")
|
|
756
|
+
continue;
|
|
757
|
+
// Anthropic: results of a search the API executed server-side.
|
|
758
|
+
if (block.type === "web_search_tool_result" &&
|
|
759
|
+
Array.isArray(block.content)) {
|
|
760
|
+
for (const result of block.content) {
|
|
761
|
+
addSource(result?.url, result?.title);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
// Anthropic: citations attached to the answer's text blocks.
|
|
765
|
+
if (Array.isArray(block.citations)) {
|
|
766
|
+
for (const citation of block.citations) {
|
|
767
|
+
addSource(citation?.url, citation?.title);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
// OpenAI Responses API: one block per executed search, plus url citations.
|
|
771
|
+
if (block.type === "web_search_call" && block.id) {
|
|
772
|
+
usage.callIds.add(block.id);
|
|
773
|
+
}
|
|
774
|
+
if (Array.isArray(block.annotations)) {
|
|
775
|
+
for (const annotation of block.annotations) {
|
|
776
|
+
if (annotation?.type === "url_citation") {
|
|
777
|
+
addSource(annotation.url, annotation.title);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
const metadata = payload.response_metadata;
|
|
784
|
+
if (!metadata)
|
|
785
|
+
return;
|
|
786
|
+
const requests = metadata.usage?.server_tool_use?.web_search_requests;
|
|
787
|
+
if (typeof requests === "number" && requests > usage.reportedRequests) {
|
|
788
|
+
usage.reportedRequests = requests;
|
|
789
|
+
}
|
|
790
|
+
const grounding = metadata.groundingMetadata;
|
|
791
|
+
if (grounding) {
|
|
792
|
+
usage.grounded = true;
|
|
793
|
+
for (const chunk of grounding.groundingChunks || []) {
|
|
794
|
+
addSource(chunk?.web?.uri, chunk?.web?.title);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
/**
|
|
799
|
+
* Adds a call's web-search usage to the caller-supplied tracker. Searches are
|
|
800
|
+
* billed per request rather than per token (Anthropic charges $10 per 1,000
|
|
801
|
+
* searches; Google charges per grounded request), so the rate is read from the
|
|
802
|
+
* `<model>-web-search-costs` constant expressed in USD per 1,000 searches and
|
|
803
|
+
* accumulated under `<model>_webSearches`. Sources are appended to
|
|
804
|
+
* `tracker.webSearchSources`, deduplicated by URL across the whole run.
|
|
805
|
+
*/
|
|
806
|
+
const updateWebSearchUsageTracker = (tracker, modelName, usage, config) => {
|
|
807
|
+
if (!tracker || !modelName)
|
|
808
|
+
return;
|
|
809
|
+
// The three signals describe the same searches from different providers, so
|
|
810
|
+
// the largest one is the count rather than their sum.
|
|
811
|
+
const searches = Math.max(usage.reportedRequests, usage.callIds.size, usage.grounded ? 1 : 0);
|
|
812
|
+
if (!searches && usage.sources.size === 0)
|
|
813
|
+
return;
|
|
814
|
+
if (typeof tracker.cost !== "number")
|
|
815
|
+
tracker.cost = 0;
|
|
816
|
+
if (!tracker.tokens || typeof tracker.tokens !== "object")
|
|
817
|
+
tracker.tokens = {};
|
|
818
|
+
if (searches > 0) {
|
|
819
|
+
const addedCost = (searches / 1_000) * getModelRate(modelName, config, "web-search");
|
|
820
|
+
if (Number.isFinite(addedCost) && addedCost > 0)
|
|
821
|
+
tracker.cost += addedCost;
|
|
822
|
+
const key = `${modelName}_webSearches`;
|
|
823
|
+
tracker.tokens[key] = (tracker.tokens[key] || 0) + searches;
|
|
824
|
+
}
|
|
825
|
+
if (usage.sources.size > 0) {
|
|
826
|
+
const sources = tracker.webSearchSources || (tracker.webSearchSources = []);
|
|
827
|
+
for (const source of usage.sources.values()) {
|
|
828
|
+
if (!sources.some((existing) => existing.url === source.url)) {
|
|
829
|
+
sources.push(source);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
};
|
|
606
834
|
/**
|
|
607
835
|
* Instantiates and returns the appropriate LangChain chat model based on the model
|
|
608
836
|
* name prefix. `claude-` maps to `ChatAnthropic`, `gemini-` maps to
|
|
@@ -629,6 +857,13 @@ const getLLMModel = (modelName, config, schema = null) => {
|
|
|
629
857
|
// Resolve `temperature` with presence/support/range handling (see
|
|
630
858
|
// resolveTemperatureSetting). Applied uniformly to every provider below.
|
|
631
859
|
const modelSettings = resolveTemperatureSetting(modelName, config);
|
|
860
|
+
// Native web search (see buildWebSearchTool). The tool is bound to the model
|
|
861
|
+
// so both `.invoke()` and `.stream()` pick it up; the provider runs the search
|
|
862
|
+
// server-side within the same request, so no client-side agent loop is needed.
|
|
863
|
+
const webSearchOptions = getWebSearchOptions(config);
|
|
864
|
+
const webSearchTool = webSearchOptions
|
|
865
|
+
? buildWebSearchTool(modelName, webSearchOptions)
|
|
866
|
+
: null;
|
|
632
867
|
// Claude models (Anthropic)
|
|
633
868
|
if (modelName.startsWith("claude-")) {
|
|
634
869
|
// Anthropic's SDK rejects non-streamed requests when max_tokens is large
|
|
@@ -653,7 +888,7 @@ const getLLMModel = (modelName, config, schema = null) => {
|
|
|
653
888
|
},
|
|
654
889
|
}
|
|
655
890
|
: {};
|
|
656
|
-
|
|
891
|
+
const model = new ChatAnthropic({
|
|
657
892
|
apiKey: config.anthropicAPIKey,
|
|
658
893
|
maxTokens,
|
|
659
894
|
modelName: modelName,
|
|
@@ -662,6 +897,7 @@ const getLLMModel = (modelName, config, schema = null) => {
|
|
|
662
897
|
...outputConfig,
|
|
663
898
|
...modelSettings,
|
|
664
899
|
});
|
|
900
|
+
return webSearchTool ? model.bindTools([webSearchTool]) : model;
|
|
665
901
|
}
|
|
666
902
|
// Gemini models (Google)
|
|
667
903
|
else if (modelName.startsWith("gemini-")) {
|
|
@@ -674,6 +910,11 @@ const getLLMModel = (modelName, config, schema = null) => {
|
|
|
674
910
|
...(schema ? { json: true } : {}),
|
|
675
911
|
...modelSettings,
|
|
676
912
|
});
|
|
913
|
+
// Combining grounding with structured output requires Gemini 3 or later;
|
|
914
|
+
// Gemini 1.5/2.x reject `responseSchema` alongside `googleSearch` with a 400.
|
|
915
|
+
const bound = webSearchTool
|
|
916
|
+
? model.bindTools([webSearchTool])
|
|
917
|
+
: model;
|
|
677
918
|
// `responseSchema` additionally constrains the output shape. It is a
|
|
678
919
|
// call-time option (not a constructor field), so it is bound onto the model
|
|
679
920
|
// via `withConfig`. Gemini's schema is an OpenAPI 3.0 subset, so
|
|
@@ -681,9 +922,9 @@ const getLLMModel = (modelName, config, schema = null) => {
|
|
|
681
922
|
// parse/validate pipeline is unchanged.
|
|
682
923
|
if (schema) {
|
|
683
924
|
const jsonSchema = sanitizeGeminiSchema(buildJsonSchema(schema));
|
|
684
|
-
return
|
|
925
|
+
return bound.withConfig({ responseSchema: jsonSchema });
|
|
685
926
|
}
|
|
686
|
-
return
|
|
927
|
+
return bound;
|
|
687
928
|
}
|
|
688
929
|
// GPT models (OpenAI)
|
|
689
930
|
else if (modelName.startsWith("gpt-")) {
|
|
@@ -691,23 +932,41 @@ const getLLMModel = (modelName, config, schema = null) => {
|
|
|
691
932
|
apiKey: config.openAIAPIKey,
|
|
692
933
|
max_tokens: config.maxTokens || 200000,
|
|
693
934
|
modelName: modelName,
|
|
935
|
+
// `web_search` is a hosted Responses API tool, so the request has to go to
|
|
936
|
+
// `/v1/responses` rather than `/v1/chat/completions`.
|
|
937
|
+
...(webSearchTool ? { useResponsesApi: true } : {}),
|
|
694
938
|
...modelSettings,
|
|
695
939
|
};
|
|
696
|
-
// Use native
|
|
940
|
+
// Use native structured output with a JSON schema. The two endpoints spell
|
|
941
|
+
// the same thing differently — Chat Completions takes `response_format`,
|
|
942
|
+
// the Responses API takes `text.format` with the schema flattened one level
|
|
943
|
+
// — and `modelKwargs` is spread verbatim into whichever request is built.
|
|
697
944
|
if (schema) {
|
|
698
945
|
const jsonSchema = strictifyJsonSchema(buildJsonSchema(schema));
|
|
699
|
-
openAISettings.modelKwargs =
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
946
|
+
openAISettings.modelKwargs = webSearchTool
|
|
947
|
+
? {
|
|
948
|
+
text: {
|
|
949
|
+
format: {
|
|
950
|
+
type: "json_schema",
|
|
951
|
+
name: "response_schema",
|
|
952
|
+
strict: true,
|
|
953
|
+
schema: jsonSchema,
|
|
954
|
+
},
|
|
706
955
|
},
|
|
707
|
-
}
|
|
708
|
-
|
|
956
|
+
}
|
|
957
|
+
: {
|
|
958
|
+
response_format: {
|
|
959
|
+
type: "json_schema",
|
|
960
|
+
json_schema: {
|
|
961
|
+
name: "response_schema",
|
|
962
|
+
strict: true,
|
|
963
|
+
schema: jsonSchema,
|
|
964
|
+
},
|
|
965
|
+
},
|
|
966
|
+
};
|
|
709
967
|
}
|
|
710
|
-
|
|
968
|
+
const model = new ChatOpenAI(openAISettings);
|
|
969
|
+
return webSearchTool ? model.bindTools([webSearchTool]) : model;
|
|
711
970
|
}
|
|
712
971
|
// OpenAI-compatible providers: DeepSeek, Kimi (Moonshot), GLM (Zhipu)
|
|
713
972
|
const openAICompatible = getOpenAICompatibleProvider(modelName, config);
|
|
@@ -736,11 +995,18 @@ const getLLMModel = (modelName, config, schema = null) => {
|
|
|
736
995
|
* @returns A configured LangChain agent instance ready to be run with `runAgent`
|
|
737
996
|
*/
|
|
738
997
|
const createAgent = (name, modelName, systemPrompt, tools = [], responseFormat, config) => {
|
|
998
|
+
// Native web search joins the agent's tool list instead of being bound inside
|
|
999
|
+
// `getLLMModel`: the agent binds its own tools to the model, which would drop
|
|
1000
|
+
// anything already bound there.
|
|
1001
|
+
const webSearchOptions = getWebSearchOptions(config);
|
|
1002
|
+
const webSearchTool = webSearchOptions
|
|
1003
|
+
? buildWebSearchTool(modelName, webSearchOptions)
|
|
1004
|
+
: null;
|
|
739
1005
|
const agent = createLangChainAgent({
|
|
740
1006
|
name: name,
|
|
741
|
-
model: getLLMModel(modelName, config),
|
|
1007
|
+
model: getLLMModel(modelName, { ...config, webSearch: null }),
|
|
742
1008
|
systemPrompt: systemPrompt.trim(),
|
|
743
|
-
tools,
|
|
1009
|
+
tools: webSearchTool ? [...tools, webSearchTool] : tools,
|
|
744
1010
|
...(responseFormat ? { responseFormat: responseFormat } : {}),
|
|
745
1011
|
});
|
|
746
1012
|
return agent;
|
|
@@ -827,6 +1093,13 @@ const runAgent = async (agent, prompt, config, onProgress = null, usageTracker =
|
|
|
827
1093
|
}
|
|
828
1094
|
}
|
|
829
1095
|
updateUsageTracker(usageTracker, modelName, sumAgentResponseUsage(response), config);
|
|
1096
|
+
// Web-search activity is spread across the agent's messages — one search may
|
|
1097
|
+
// be reported by the message that ran it and cited by a later one.
|
|
1098
|
+
const webSearchUsage = createWebSearchUsage();
|
|
1099
|
+
for (const message of response?.messages || []) {
|
|
1100
|
+
collectWebSearchUsage(message, webSearchUsage);
|
|
1101
|
+
}
|
|
1102
|
+
updateWebSearchUsageTracker(usageTracker, modelName, webSearchUsage, config);
|
|
830
1103
|
const endTime = Date.now();
|
|
831
1104
|
const duration = endTime - startTime;
|
|
832
1105
|
logger.log(null, logger.levels.info, `Agent "${agent.options?.name}" completed in ${Math.round(duration / 1000)} seconds.`);
|
|
@@ -1063,10 +1336,16 @@ const buildValidationRetryMessages = (priorMessages, rawContent, validationError
|
|
|
1063
1336
|
*
|
|
1064
1337
|
* When `expectsJsonResponse` is `true`, JSON escape instructions are prepended to the
|
|
1065
1338
|
* system prompt and the parsed result is optionally validated against `schema`.
|
|
1339
|
+
*
|
|
1340
|
+
* Setting `config.webSearch` enables the provider's native web search tool in every
|
|
1341
|
+
* mode (see `buildWebSearchTool`). The provider runs the search server-side inside the
|
|
1342
|
+
* same request, so the return contract is unchanged; the searches performed and the
|
|
1343
|
+
* sources cited are recorded on `usageTracker`.
|
|
1066
1344
|
* @param modelName - The model identifier, e.g. `"gpt-4o"`, `"claude-3-5-sonnet"`,
|
|
1067
1345
|
* `"gemini-1.5-pro"`
|
|
1068
1346
|
* @param config - Configuration object with API keys, `temperature`, optional `agentic`
|
|
1069
|
-
* flag, and optional `
|
|
1347
|
+
* flag, optional `recursionLimit`, and optional `webSearch` (`true` or a
|
|
1348
|
+
* `WebSearchConfig`)
|
|
1070
1349
|
* @param prompt - The prompt to send; either a plain string (user message only) or an
|
|
1071
1350
|
* array of `{ role, content }` message objects
|
|
1072
1351
|
* @param onProgressReport - Optional async callback invoked with `{ message, progress }`
|
|
@@ -1128,12 +1407,9 @@ const runPromptWithModel = async (modelName, config, prompt, onProgressReport, m
|
|
|
1128
1407
|
throw errorHandlingHelper.create(constants.HTTP_CODES.INTERNAL_SERVER_ERROR, "Agent returned no messages");
|
|
1129
1408
|
}
|
|
1130
1409
|
const lastMessage = messages[messages.length - 1];
|
|
1131
|
-
|
|
1132
|
-
//
|
|
1133
|
-
|
|
1134
|
-
const textBlock = rawContent.find((block) => typeof block === "object" && block.type === "text");
|
|
1135
|
-
rawContent = textBlock?.text || "";
|
|
1136
|
-
}
|
|
1410
|
+
// Flattens the array content blocks that Gemini/Claude agent responses and
|
|
1411
|
+
// any server-tool turn (e.g. web search) return.
|
|
1412
|
+
const rawContent = extractTextContent(lastMessage?.content);
|
|
1137
1413
|
// If not expecting JSON, return raw content directly
|
|
1138
1414
|
if (!expectsJsonResponse) {
|
|
1139
1415
|
return rawContent;
|
|
@@ -1255,6 +1531,7 @@ const runPromptWithModel = async (modelName, config, prompt, onProgressReport, m
|
|
|
1255
1531
|
output_tokens: 0,
|
|
1256
1532
|
total_tokens: 0,
|
|
1257
1533
|
};
|
|
1534
|
+
let webSearchUsage = createWebSearchUsage();
|
|
1258
1535
|
// Inner loop: wait + retry on 429 around stream setup and consumption.
|
|
1259
1536
|
// Usage is only recorded on a successful stream — partial streams that
|
|
1260
1537
|
// error out with a rate limit are not counted. A 429 fired mid-stream
|
|
@@ -1264,6 +1541,7 @@ const runPromptWithModel = async (modelName, config, prompt, onProgressReport, m
|
|
|
1264
1541
|
rawContent = "";
|
|
1265
1542
|
chunkCount = 0;
|
|
1266
1543
|
streamUsage = { input_tokens: 0, output_tokens: 0, total_tokens: 0 };
|
|
1544
|
+
webSearchUsage = createWebSearchUsage();
|
|
1267
1545
|
try {
|
|
1268
1546
|
// Honour caller cancellation: passing the signal tears down the
|
|
1269
1547
|
// upstream HTTP request so a cancelled call stops billing tokens.
|
|
@@ -1276,16 +1554,16 @@ const runPromptWithModel = async (modelName, config, prompt, onProgressReport, m
|
|
|
1276
1554
|
throw errorHandlingHelper.create(constants.HTTP_CODES.BAD_REQUEST, "Request cancelled by caller");
|
|
1277
1555
|
}
|
|
1278
1556
|
accumulateChunkUsage(streamUsage, chunk);
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
}
|
|
1557
|
+
collectWebSearchUsage(chunk, webSearchUsage);
|
|
1558
|
+
// Counting every chunk (not just the ones carrying text) keeps
|
|
1559
|
+
// progress ticking through the pause while a search runs.
|
|
1560
|
+
chunkCount++;
|
|
1561
|
+
rawContent += extractTextContent(chunk?.content ?? chunk);
|
|
1562
|
+
if (chunkCount % progressReportInterval === 0) {
|
|
1563
|
+
await onProgressReport({
|
|
1564
|
+
message: "Generating content...",
|
|
1565
|
+
progress: Math.min(calcCurrentProgress(), maxPercent - 5),
|
|
1566
|
+
});
|
|
1289
1567
|
}
|
|
1290
1568
|
}
|
|
1291
1569
|
break; // stream completed without 429
|
|
@@ -1308,6 +1586,7 @@ const runPromptWithModel = async (modelName, config, prompt, onProgressReport, m
|
|
|
1308
1586
|
}
|
|
1309
1587
|
}
|
|
1310
1588
|
updateUsageTracker(usageTracker, modelName, streamUsage, config);
|
|
1589
|
+
updateWebSearchUsageTracker(usageTracker, modelName, webSearchUsage, config);
|
|
1311
1590
|
if (!rawContent) {
|
|
1312
1591
|
throw errorHandlingHelper.create(constants.HTTP_CODES.INTERNAL_SERVER_ERROR, "LLM returned no content");
|
|
1313
1592
|
}
|
|
@@ -1412,7 +1691,11 @@ const runPromptWithModel = async (modelName, config, prompt, onProgressReport, m
|
|
|
1412
1691
|
}
|
|
1413
1692
|
}
|
|
1414
1693
|
updateUsageTracker(usageTracker, modelName, extractUsageFromInvoke(response), config);
|
|
1415
|
-
const
|
|
1694
|
+
const webSearchUsage = createWebSearchUsage();
|
|
1695
|
+
collectWebSearchUsage(response, webSearchUsage);
|
|
1696
|
+
updateWebSearchUsageTracker(usageTracker, modelName, webSearchUsage, config);
|
|
1697
|
+
// Flattened because a server-tool turn returns content blocks, not a string.
|
|
1698
|
+
const rawContent = extractTextContent(response?.content ?? response);
|
|
1416
1699
|
// If not expecting JSON, return raw content directly
|
|
1417
1700
|
if (!expectsJsonResponse) {
|
|
1418
1701
|
return rawContent;
|
package/dist/esm/serve.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,eAAe,CAAC;AA+PtC;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;AAE/D;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,OAAO,EAAE,GAAG,KACT,OAAO,CAAC;IAAE,IAAI,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC,CAAC;AAEzC,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wFAAwF;IACxF,YAAY,CAAC,EAAE,aAAa,CAAC;CAC9B;AAyHD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,GAChB,MAAM,SAAS,EACf,UAAS,YAAiB,KACzB,IAAI,CAAC,MA8MP,CAAC"}
|