@stackfactor/agent-utils 1.1.1 → 1.1.3
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/agentProto.d.ts +19 -0
- package/dist/cjs/agentProto.d.ts.map +1 -0
- package/dist/cjs/agentProto.js +98 -0
- package/dist/cjs/client.d.ts +41 -0
- package/dist/cjs/client.d.ts.map +1 -0
- package/dist/cjs/client.js +131 -0
- package/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/langChain.d.ts.map +1 -1
- package/dist/cjs/langChain.js +20 -7
- package/dist/cjs/serve.d.ts.map +1 -1
- package/dist/cjs/serve.js +2 -54
- package/dist/esm/agentProto.d.ts +19 -0
- package/dist/esm/agentProto.d.ts.map +1 -0
- package/dist/esm/agentProto.js +60 -0
- package/dist/esm/client.d.ts +41 -0
- package/dist/esm/client.d.ts.map +1 -0
- package/dist/esm/client.js +94 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/langChain.d.ts.map +1 -1
- package/dist/esm/langChain.js +20 -7
- package/dist/esm/serve.d.ts.map +1 -1
- package/dist/esm/serve.js +1 -53
- package/package.json +3 -3
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as grpc from "@grpc/grpc-js";
|
|
2
|
+
/**
|
|
3
|
+
* gRPC contract shared by the entire agent fleet — the `serve()` server AND the
|
|
4
|
+
* relay client. `data`/`config`/`request` are carried as JSON strings so
|
|
5
|
+
* arbitrary payloads need no proto schema churn. `Execute` is server-streaming:
|
|
6
|
+
* zero or more `Progress` frames (driven by the agent's `onProgress`) followed
|
|
7
|
+
* by exactly one `Result` or `Error`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const PROTO = "\nsyntax = \"proto3\";\npackage stackfactor.agent.v1;\n\nservice Agent {\n rpc Execute(ExecuteRequest) returns (stream Update);\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 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
|
+
/**
|
|
11
|
+
* Loads the Agent proto package. proto-loader reads from a file, so the embedded
|
|
12
|
+
* schema is written to a temp path — keeps the package self-contained across the
|
|
13
|
+
* cjs/esm dual build. Returns the loaded package (server uses
|
|
14
|
+
* `.stackfactor.agent.v1.Agent.service`; client uses the `Agent` constructor).
|
|
15
|
+
*/
|
|
16
|
+
export declare const loadAgentPackage: () => any;
|
|
17
|
+
/** The Agent gRPC service definition (for the `serve()` server). */
|
|
18
|
+
export declare const loadAgentService: () => grpc.ServiceDefinition;
|
|
19
|
+
//# sourceMappingURL=agentProto.d.ts.map
|
|
@@ -0,0 +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,smBA2BjB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,QAAO,GAWnC,CAAC;AAEF,oEAAoE;AACpE,eAAO,MAAM,gBAAgB,QAAO,IAAI,CAAC,iBACc,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.loadAgentService = exports.loadAgentPackage = exports.PROTO = void 0;
|
|
37
|
+
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
38
|
+
const protoLoader = __importStar(require("@grpc/proto-loader"));
|
|
39
|
+
const node_fs_1 = require("node:fs");
|
|
40
|
+
const node_path_1 = require("node:path");
|
|
41
|
+
const node_os_1 = require("node:os");
|
|
42
|
+
/**
|
|
43
|
+
* gRPC contract shared by the entire agent fleet — the `serve()` server AND the
|
|
44
|
+
* relay client. `data`/`config`/`request` are carried as JSON strings so
|
|
45
|
+
* arbitrary payloads need no proto schema churn. `Execute` is server-streaming:
|
|
46
|
+
* zero or more `Progress` frames (driven by the agent's `onProgress`) followed
|
|
47
|
+
* by exactly one `Result` or `Error`.
|
|
48
|
+
*/
|
|
49
|
+
exports.PROTO = `
|
|
50
|
+
syntax = "proto3";
|
|
51
|
+
package stackfactor.agent.v1;
|
|
52
|
+
|
|
53
|
+
service Agent {
|
|
54
|
+
rpc Execute(ExecuteRequest) returns (stream Update);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
message ExecuteRequest {
|
|
58
|
+
string content_type = 1;
|
|
59
|
+
string data_json = 2;
|
|
60
|
+
string config_json = 3;
|
|
61
|
+
string request_json = 4;
|
|
62
|
+
int32 event = 5;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
message Update {
|
|
66
|
+
oneof payload {
|
|
67
|
+
Progress progress = 1;
|
|
68
|
+
Result result = 2;
|
|
69
|
+
ErrorInfo error = 3;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
message Progress { int32 progress = 1; string message = 2; }
|
|
74
|
+
message Result { string result_json = 1; }
|
|
75
|
+
message ErrorInfo { int32 code = 1; string message = 2; }
|
|
76
|
+
`;
|
|
77
|
+
/**
|
|
78
|
+
* Loads the Agent proto package. proto-loader reads from a file, so the embedded
|
|
79
|
+
* schema is written to a temp path — keeps the package self-contained across the
|
|
80
|
+
* cjs/esm dual build. Returns the loaded package (server uses
|
|
81
|
+
* `.stackfactor.agent.v1.Agent.service`; client uses the `Agent` constructor).
|
|
82
|
+
*/
|
|
83
|
+
const loadAgentPackage = () => {
|
|
84
|
+
const file = (0, node_path_1.join)((0, node_os_1.tmpdir)(), "stackfactor-agent.v1.proto");
|
|
85
|
+
(0, node_fs_1.writeFileSync)(file, exports.PROTO);
|
|
86
|
+
const packageDefinition = protoLoader.loadSync(file, {
|
|
87
|
+
keepCase: true,
|
|
88
|
+
longs: String,
|
|
89
|
+
enums: String,
|
|
90
|
+
defaults: true,
|
|
91
|
+
oneofs: true,
|
|
92
|
+
});
|
|
93
|
+
return grpc.loadPackageDefinition(packageDefinition);
|
|
94
|
+
};
|
|
95
|
+
exports.loadAgentPackage = loadAgentPackage;
|
|
96
|
+
/** The Agent gRPC service definition (for the `serve()` server). */
|
|
97
|
+
const loadAgentService = () => (0, exports.loadAgentPackage)().stackfactor.agent.v1.Agent.service;
|
|
98
|
+
exports.loadAgentService = loadAgentService;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fleet `Agent.Execute` request. `*_json` fields are JSON strings.
|
|
3
|
+
* `request_json` MUST include the caller's StackFactor session token as
|
|
4
|
+
* `authToken` — the agent validates it (via `serve()`'s auth gate) before
|
|
5
|
+
* running. This is distinct from the Cloud Run ID token (see `CallAgentOptions`).
|
|
6
|
+
*/
|
|
7
|
+
export interface AgentExecuteRequest {
|
|
8
|
+
content_type: string;
|
|
9
|
+
data_json: string;
|
|
10
|
+
config_json: string;
|
|
11
|
+
request_json: string;
|
|
12
|
+
event: number;
|
|
13
|
+
}
|
|
14
|
+
export interface CallAgentOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Google ID token (audience = the agent's Cloud Run URL) to get past Cloud
|
|
17
|
+
* Run's `--no-allow-unauthenticated` ingress. Attached as `authorization:
|
|
18
|
+
* Bearer` metadata. The agent never sees it; ingress consumes it.
|
|
19
|
+
*/
|
|
20
|
+
idToken?: string;
|
|
21
|
+
/** Called for each streamed `Progress` frame (e.g. to forward to a socket). */
|
|
22
|
+
onProgress?: (update: {
|
|
23
|
+
progress: number;
|
|
24
|
+
message: string;
|
|
25
|
+
}) => void;
|
|
26
|
+
/** Abort the RPC (e.g. the frontend socket dropped) → stops the agent's LLM. */
|
|
27
|
+
signal?: AbortSignal;
|
|
28
|
+
/** Call deadline; defaults to 900s to match Cloud Run's max request timeout. */
|
|
29
|
+
deadlineMs?: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Calls a deployed agent's `Agent.Execute` over gRPC server-streaming. Forwards
|
|
33
|
+
* each `Progress` frame to `onProgress`, resolves with the parsed `result_json`
|
|
34
|
+
* on `Result`, and rejects (with `error.code`) on `ErrorInfo`. Caller-cancel via
|
|
35
|
+
* `options.signal` cancels the RPC.
|
|
36
|
+
*
|
|
37
|
+
* @param agentUrl - The agent's Cloud Run URL (https://…run.app).
|
|
38
|
+
* @param request - The `ExecuteRequest` (its `request_json` carries `authToken`).
|
|
39
|
+
*/
|
|
40
|
+
export declare const callAgent: (agentUrl: string, request: AgentExecuteRequest, options?: CallAgentOptions) => Promise<any>;
|
|
41
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +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;AAqCD;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,GACpB,UAAU,MAAM,EAChB,SAAS,mBAAmB,EAC5B,UAAS,gBAAqB,KAC7B,OAAO,CAAC,GAAG,CAyDV,CAAC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.callAgent = void 0;
|
|
37
|
+
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
38
|
+
const agentProto_js_1 = require("./agentProto.js");
|
|
39
|
+
const DEFAULT_DEADLINE_MS = 900_000;
|
|
40
|
+
const safeParse = (value, fallback) => {
|
|
41
|
+
if (!value)
|
|
42
|
+
return fallback;
|
|
43
|
+
try {
|
|
44
|
+
return JSON.parse(value);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return fallback;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
/** Turns an `ErrorInfo` frame into an Error carrying the agent's `code`. */
|
|
51
|
+
const agentError = (frame) => {
|
|
52
|
+
const error = new Error(frame?.message || "Agent error");
|
|
53
|
+
error.code = frame?.code;
|
|
54
|
+
return error;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Channel credentials for an authenticated Cloud Run agent: TLS (Cloud Run
|
|
58
|
+
* terminates at the edge) plus per-call metadata carrying the ID token.
|
|
59
|
+
*/
|
|
60
|
+
const buildCredentials = (idToken) => {
|
|
61
|
+
const ssl = grpc.credentials.createSsl();
|
|
62
|
+
if (!idToken) {
|
|
63
|
+
return ssl;
|
|
64
|
+
}
|
|
65
|
+
const callCreds = grpc.credentials.createFromMetadataGenerator((_ctx, cb) => {
|
|
66
|
+
const metadata = new grpc.Metadata();
|
|
67
|
+
metadata.add("authorization", `Bearer ${idToken}`);
|
|
68
|
+
cb(null, metadata);
|
|
69
|
+
});
|
|
70
|
+
return grpc.credentials.combineChannelCredentials(ssl, callCreds);
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Calls a deployed agent's `Agent.Execute` over gRPC server-streaming. Forwards
|
|
74
|
+
* each `Progress` frame to `onProgress`, resolves with the parsed `result_json`
|
|
75
|
+
* on `Result`, and rejects (with `error.code`) on `ErrorInfo`. Caller-cancel via
|
|
76
|
+
* `options.signal` cancels the RPC.
|
|
77
|
+
*
|
|
78
|
+
* @param agentUrl - The agent's Cloud Run URL (https://…run.app).
|
|
79
|
+
* @param request - The `ExecuteRequest` (its `request_json` carries `authToken`).
|
|
80
|
+
*/
|
|
81
|
+
const callAgent = (agentUrl, request, options = {}) => new Promise((resolve, reject) => {
|
|
82
|
+
const loaded = (0, agentProto_js_1.loadAgentPackage)();
|
|
83
|
+
const url = new URL(agentUrl);
|
|
84
|
+
const address = `${url.hostname}:${url.port || "443"}`;
|
|
85
|
+
const client = new loaded.stackfactor.agent.v1.Agent(address, buildCredentials(options.idToken));
|
|
86
|
+
const stream = client.Execute(request, {
|
|
87
|
+
deadline: Date.now() + (options.deadlineMs ?? DEFAULT_DEADLINE_MS),
|
|
88
|
+
});
|
|
89
|
+
let settled = false;
|
|
90
|
+
const settle = (run) => {
|
|
91
|
+
if (settled) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
settled = true;
|
|
95
|
+
try {
|
|
96
|
+
client.close();
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// ignore close errors
|
|
100
|
+
}
|
|
101
|
+
run();
|
|
102
|
+
};
|
|
103
|
+
if (options.signal) {
|
|
104
|
+
if (options.signal.aborted) {
|
|
105
|
+
stream.cancel();
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
options.signal.addEventListener("abort", () => stream.cancel(), {
|
|
109
|
+
once: true,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
stream.on("data", (update) => {
|
|
114
|
+
if (update.progress) {
|
|
115
|
+
options.onProgress?.({
|
|
116
|
+
progress: update.progress.progress ?? 0,
|
|
117
|
+
message: update.progress.message ?? "",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
else if (update.result) {
|
|
121
|
+
const result = safeParse(update.result.result_json, null);
|
|
122
|
+
settle(() => resolve(result));
|
|
123
|
+
}
|
|
124
|
+
else if (update.error) {
|
|
125
|
+
settle(() => reject(agentError(update.error)));
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
stream.on("error", (error) => settle(() => reject(error)));
|
|
129
|
+
stream.on("end", () => settle(() => reject(new Error("Agent stream ended without a result or error"))));
|
|
130
|
+
});
|
|
131
|
+
exports.callAgent = callAgent;
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -3,11 +3,14 @@ import errorHandling from "./errorHandling.js";
|
|
|
3
3
|
import langChain from "./langChain.js";
|
|
4
4
|
import logger from "./logger.js";
|
|
5
5
|
import { serve } from "./serve.js";
|
|
6
|
+
import { callAgent } from "./client.js";
|
|
6
7
|
import * as runtimeContext from "./runtimeContext.js";
|
|
7
8
|
export { constants };
|
|
8
9
|
export { errorHandling };
|
|
9
10
|
export { langChain };
|
|
10
11
|
export { logger };
|
|
11
12
|
export { serve };
|
|
13
|
+
export { callAgent };
|
|
14
|
+
export type { AgentExecuteRequest, CallAgentOptions } from "./client.js";
|
|
12
15
|
export { runtimeContext };
|
|
13
16
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/cjs/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,MAAM,oBAAoB,CAAC;AAC/C,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,aAAa,EAAE,CAAC;AAEzB,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,MAAM,EAAE,CAAC;AAElB,OAAO,EAAE,KAAK,EAAE,CAAC;AAEjB,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,aAAa,MAAM,oBAAoB,CAAC;AAC/C,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,aAAa,EAAE,CAAC;AAEzB,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,MAAM,EAAE,CAAC;AAElB,OAAO,EAAE,KAAK,EAAE,CAAC;AAEjB,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEzE,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
package/dist/cjs/index.js
CHANGED
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.runtimeContext = exports.serve = exports.logger = exports.langChain = exports.errorHandling = exports.constants = void 0;
|
|
39
|
+
exports.runtimeContext = exports.callAgent = exports.serve = exports.logger = exports.langChain = exports.errorHandling = exports.constants = void 0;
|
|
40
40
|
const const_js_1 = __importDefault(require("./const.js"));
|
|
41
41
|
exports.constants = const_js_1.default;
|
|
42
42
|
const errorHandling_js_1 = __importDefault(require("./errorHandling.js"));
|
|
@@ -47,5 +47,7 @@ const logger_js_1 = __importDefault(require("./logger.js"));
|
|
|
47
47
|
exports.logger = logger_js_1.default;
|
|
48
48
|
const serve_js_1 = require("./serve.js");
|
|
49
49
|
Object.defineProperty(exports, "serve", { enumerable: true, get: function () { return serve_js_1.serve; } });
|
|
50
|
+
const client_js_1 = require("./client.js");
|
|
51
|
+
Object.defineProperty(exports, "callAgent", { enumerable: true, get: function () { return client_js_1.callAgent; } });
|
|
50
52
|
const runtimeContext = __importStar(require("./runtimeContext.js"));
|
|
51
53
|
exports.runtimeContext = runtimeContext;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":";0CAiD6C,GAAG,KAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":";0CAiD6C,GAAG,KAAG,IAAI;wBAyhB/C,MAAM,aACD,MAAM,gBACH,MAAM,SACb,GAAG,EAAE,kBACI,GAAG,UACX,GAAG,KACV,GAAG;sBAyBG,GAAG,UACF,MAAM,UACN,GAAG,eACC,QAAQ,GAAG,IAAI,KAC1B,OAAO,CAAC,GAAG,CAAC;oCAqXF,MAAM,UACT,GAAG,UACH,GAAG,oBACO,GAAG,eACT,MAAM,eACN,MAAM,wBACG,OAAO,WACpB,GAAG,cACA,MAAM,UACV,GAAG,EAAE,KACX,OAAO,CAAC,GAAG,CAAC;sDAosBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,KACX,OAAO,CAAC,GAAG,CAAC;0CAn+B8B,GAAG,KAAG,MAAM;mCAoBxC,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AAwgCT,wBAQE"}
|
package/dist/cjs/langChain.js
CHANGED
|
@@ -81,11 +81,11 @@ const getModelRate = (modelName, config, kind) => {
|
|
|
81
81
|
return Number.isFinite(rate) ? rate : null;
|
|
82
82
|
};
|
|
83
83
|
/**
|
|
84
|
-
* Accumulates the per-model
|
|
85
|
-
* agent can report input
|
|
86
|
-
* quota global is absent.
|
|
84
|
+
* Accumulates the per-model usage breakdown on `global.quota.byModel` so the
|
|
85
|
+
* agent can report input/output token counts and image spend per model to the
|
|
86
|
+
* UI. No-ops when the quota global is absent.
|
|
87
87
|
*/
|
|
88
|
-
const recordModelBreakdown = (modelName, inputCost, outputCost, imageCost) => {
|
|
88
|
+
const recordModelBreakdown = (modelName, inputCost, outputCost, imageCost, inputTokens = 0, outputTokens = 0) => {
|
|
89
89
|
const quota = globalThis.quota;
|
|
90
90
|
if (!quota || !modelName)
|
|
91
91
|
return;
|
|
@@ -95,13 +95,24 @@ const recordModelBreakdown = (modelName, inputCost, outputCost, imageCost) => {
|
|
|
95
95
|
inputCost: 0,
|
|
96
96
|
outputCost: 0,
|
|
97
97
|
imageCost: 0,
|
|
98
|
+
inputTokens: 0,
|
|
99
|
+
outputTokens: 0,
|
|
98
100
|
});
|
|
101
|
+
// Entries created by older versions of this module lack the token counters.
|
|
102
|
+
if (typeof entry.inputTokens !== "number")
|
|
103
|
+
entry.inputTokens = 0;
|
|
104
|
+
if (typeof entry.outputTokens !== "number")
|
|
105
|
+
entry.outputTokens = 0;
|
|
99
106
|
if (Number.isFinite(inputCost) && inputCost > 0)
|
|
100
107
|
entry.inputCost += inputCost;
|
|
101
108
|
if (Number.isFinite(outputCost) && outputCost > 0)
|
|
102
109
|
entry.outputCost += outputCost;
|
|
103
110
|
if (Number.isFinite(imageCost) && imageCost > 0)
|
|
104
111
|
entry.imageCost += imageCost;
|
|
112
|
+
if (Number.isFinite(inputTokens) && inputTokens > 0)
|
|
113
|
+
entry.inputTokens += inputTokens;
|
|
114
|
+
if (Number.isFinite(outputTokens) && outputTokens > 0)
|
|
115
|
+
entry.outputTokens += outputTokens;
|
|
105
116
|
};
|
|
106
117
|
/**
|
|
107
118
|
* Computes and records the USD cost of a text LLM call from the
|
|
@@ -115,10 +126,12 @@ const recordTextCost = (modelName, usage, config) => {
|
|
|
115
126
|
return;
|
|
116
127
|
const inputRate = getModelRate(modelName, config, "input-token") || 0;
|
|
117
128
|
const outputRate = getModelRate(modelName, config, "output-token") || 0;
|
|
118
|
-
const
|
|
119
|
-
const
|
|
129
|
+
const inputTokens = usage.input_tokens || 0;
|
|
130
|
+
const outputTokens = usage.output_tokens || 0;
|
|
131
|
+
const inputCost = (inputTokens / 1_000_000) * inputRate;
|
|
132
|
+
const outputCost = (outputTokens / 1_000_000) * outputRate;
|
|
120
133
|
recordCost(inputCost + outputCost);
|
|
121
|
-
recordModelBreakdown(modelName, inputCost, outputCost, 0);
|
|
134
|
+
recordModelBreakdown(modelName, inputCost, outputCost, 0, inputTokens, outputTokens);
|
|
122
135
|
};
|
|
123
136
|
/**
|
|
124
137
|
* Computes and records the USD cost of an image generation call from the
|
package/dist/cjs/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;AAMtC;;;;;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,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC;IAAE,IAAI,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC,CAAC;AAElF,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;AAkDD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,GAAI,MAAM,SAAS,EAAE,UAAS,YAAiB,KAAG,IAAI,CAAC,MAsGxE,CAAC"}
|
package/dist/cjs/serve.js
CHANGED
|
@@ -38,12 +38,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.serve = void 0;
|
|
40
40
|
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
41
|
-
const protoLoader = __importStar(require("@grpc/proto-loader"));
|
|
42
41
|
const client_api_1 = require("@stackfactor/client-api");
|
|
43
|
-
const node_fs_1 = require("node:fs");
|
|
44
|
-
const node_path_1 = require("node:path");
|
|
45
|
-
const node_os_1 = require("node:os");
|
|
46
42
|
const logger_js_1 = __importDefault(require("./logger.js"));
|
|
43
|
+
const agentProto_js_1 = require("./agentProto.js");
|
|
47
44
|
const runtimeContext_js_1 = require("./runtimeContext.js");
|
|
48
45
|
/** Marks an auth failure so the handler can map it to gRPC UNAUTHENTICATED. */
|
|
49
46
|
class UnauthenticatedError extends Error {
|
|
@@ -66,55 +63,6 @@ const defaultAuthenticate = async (request) => {
|
|
|
66
63
|
throw new UnauthenticatedError(`Invalid auth token: ${error?.message ?? error}`);
|
|
67
64
|
}
|
|
68
65
|
};
|
|
69
|
-
/**
|
|
70
|
-
* gRPC contract shared by the entire agent fleet. `data`/`config`/`request` are
|
|
71
|
-
* carried as JSON strings so arbitrary payloads need no proto schema churn.
|
|
72
|
-
* `Execute` is server-streaming: zero or more `Progress` frames (driven by the
|
|
73
|
-
* agent's `onProgress`) followed by exactly one `Result` or `Error`.
|
|
74
|
-
*/
|
|
75
|
-
const PROTO = `
|
|
76
|
-
syntax = "proto3";
|
|
77
|
-
package stackfactor.agent.v1;
|
|
78
|
-
|
|
79
|
-
service Agent {
|
|
80
|
-
rpc Execute(ExecuteRequest) returns (stream Update);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
message ExecuteRequest {
|
|
84
|
-
string content_type = 1;
|
|
85
|
-
string data_json = 2;
|
|
86
|
-
string config_json = 3;
|
|
87
|
-
string request_json = 4;
|
|
88
|
-
int32 event = 5;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
message Update {
|
|
92
|
-
oneof payload {
|
|
93
|
-
Progress progress = 1;
|
|
94
|
-
Result result = 2;
|
|
95
|
-
ErrorInfo error = 3;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
message Progress { int32 progress = 1; string message = 2; }
|
|
100
|
-
message Result { string result_json = 1; }
|
|
101
|
-
message ErrorInfo { int32 code = 1; string message = 2; }
|
|
102
|
-
`;
|
|
103
|
-
const loadAgentService = () => {
|
|
104
|
-
// proto-loader reads from a file; write the embedded schema to a temp path so
|
|
105
|
-
// the package stays self-contained across the cjs/esm dual build.
|
|
106
|
-
const file = (0, node_path_1.join)((0, node_os_1.tmpdir)(), "stackfactor-agent.v1.proto");
|
|
107
|
-
(0, node_fs_1.writeFileSync)(file, PROTO);
|
|
108
|
-
const packageDefinition = protoLoader.loadSync(file, {
|
|
109
|
-
keepCase: true,
|
|
110
|
-
longs: String,
|
|
111
|
-
enums: String,
|
|
112
|
-
defaults: true,
|
|
113
|
-
oneofs: true,
|
|
114
|
-
});
|
|
115
|
-
const loaded = grpc.loadPackageDefinition(packageDefinition);
|
|
116
|
-
return loaded.stackfactor.agent.v1.Agent.service;
|
|
117
|
-
};
|
|
118
66
|
const safeParse = (value, fallback) => {
|
|
119
67
|
if (!value)
|
|
120
68
|
return fallback;
|
|
@@ -219,7 +167,7 @@ const serve = (main, options = {}) => {
|
|
|
219
167
|
void handle();
|
|
220
168
|
};
|
|
221
169
|
const server = new grpc.Server();
|
|
222
|
-
server.addService(loadAgentService(), { Execute: execute });
|
|
170
|
+
server.addService((0, agentProto_js_1.loadAgentService)(), { Execute: execute });
|
|
223
171
|
// Insecure: Cloud Run terminates TLS at the edge and forwards h2c to the
|
|
224
172
|
// container (requires the service to be deployed with --use-http2).
|
|
225
173
|
server.bindAsync(`${host}:${port}`, grpc.ServerCredentials.createInsecure(), (err, boundPort) => {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as grpc from "@grpc/grpc-js";
|
|
2
|
+
/**
|
|
3
|
+
* gRPC contract shared by the entire agent fleet — the `serve()` server AND the
|
|
4
|
+
* relay client. `data`/`config`/`request` are carried as JSON strings so
|
|
5
|
+
* arbitrary payloads need no proto schema churn. `Execute` is server-streaming:
|
|
6
|
+
* zero or more `Progress` frames (driven by the agent's `onProgress`) followed
|
|
7
|
+
* by exactly one `Result` or `Error`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const PROTO = "\nsyntax = \"proto3\";\npackage stackfactor.agent.v1;\n\nservice Agent {\n rpc Execute(ExecuteRequest) returns (stream Update);\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 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
|
+
/**
|
|
11
|
+
* Loads the Agent proto package. proto-loader reads from a file, so the embedded
|
|
12
|
+
* schema is written to a temp path — keeps the package self-contained across the
|
|
13
|
+
* cjs/esm dual build. Returns the loaded package (server uses
|
|
14
|
+
* `.stackfactor.agent.v1.Agent.service`; client uses the `Agent` constructor).
|
|
15
|
+
*/
|
|
16
|
+
export declare const loadAgentPackage: () => any;
|
|
17
|
+
/** The Agent gRPC service definition (for the `serve()` server). */
|
|
18
|
+
export declare const loadAgentService: () => grpc.ServiceDefinition;
|
|
19
|
+
//# sourceMappingURL=agentProto.d.ts.map
|
|
@@ -0,0 +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,smBA2BjB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,QAAO,GAWnC,CAAC;AAEF,oEAAoE;AACpE,eAAO,MAAM,gBAAgB,QAAO,IAAI,CAAC,iBACc,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as grpc from "@grpc/grpc-js";
|
|
2
|
+
import * as protoLoader from "@grpc/proto-loader";
|
|
3
|
+
import { writeFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
/**
|
|
7
|
+
* gRPC contract shared by the entire agent fleet — the `serve()` server AND the
|
|
8
|
+
* relay client. `data`/`config`/`request` are carried as JSON strings so
|
|
9
|
+
* arbitrary payloads need no proto schema churn. `Execute` is server-streaming:
|
|
10
|
+
* zero or more `Progress` frames (driven by the agent's `onProgress`) followed
|
|
11
|
+
* by exactly one `Result` or `Error`.
|
|
12
|
+
*/
|
|
13
|
+
export const PROTO = `
|
|
14
|
+
syntax = "proto3";
|
|
15
|
+
package stackfactor.agent.v1;
|
|
16
|
+
|
|
17
|
+
service Agent {
|
|
18
|
+
rpc Execute(ExecuteRequest) returns (stream Update);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
message ExecuteRequest {
|
|
22
|
+
string content_type = 1;
|
|
23
|
+
string data_json = 2;
|
|
24
|
+
string config_json = 3;
|
|
25
|
+
string request_json = 4;
|
|
26
|
+
int32 event = 5;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
message Update {
|
|
30
|
+
oneof payload {
|
|
31
|
+
Progress progress = 1;
|
|
32
|
+
Result result = 2;
|
|
33
|
+
ErrorInfo error = 3;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
message Progress { int32 progress = 1; string message = 2; }
|
|
38
|
+
message Result { string result_json = 1; }
|
|
39
|
+
message ErrorInfo { int32 code = 1; string message = 2; }
|
|
40
|
+
`;
|
|
41
|
+
/**
|
|
42
|
+
* Loads the Agent proto package. proto-loader reads from a file, so the embedded
|
|
43
|
+
* schema is written to a temp path — keeps the package self-contained across the
|
|
44
|
+
* cjs/esm dual build. Returns the loaded package (server uses
|
|
45
|
+
* `.stackfactor.agent.v1.Agent.service`; client uses the `Agent` constructor).
|
|
46
|
+
*/
|
|
47
|
+
export const loadAgentPackage = () => {
|
|
48
|
+
const file = join(tmpdir(), "stackfactor-agent.v1.proto");
|
|
49
|
+
writeFileSync(file, PROTO);
|
|
50
|
+
const packageDefinition = protoLoader.loadSync(file, {
|
|
51
|
+
keepCase: true,
|
|
52
|
+
longs: String,
|
|
53
|
+
enums: String,
|
|
54
|
+
defaults: true,
|
|
55
|
+
oneofs: true,
|
|
56
|
+
});
|
|
57
|
+
return grpc.loadPackageDefinition(packageDefinition);
|
|
58
|
+
};
|
|
59
|
+
/** The Agent gRPC service definition (for the `serve()` server). */
|
|
60
|
+
export const loadAgentService = () => loadAgentPackage().stackfactor.agent.v1.Agent.service;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fleet `Agent.Execute` request. `*_json` fields are JSON strings.
|
|
3
|
+
* `request_json` MUST include the caller's StackFactor session token as
|
|
4
|
+
* `authToken` — the agent validates it (via `serve()`'s auth gate) before
|
|
5
|
+
* running. This is distinct from the Cloud Run ID token (see `CallAgentOptions`).
|
|
6
|
+
*/
|
|
7
|
+
export interface AgentExecuteRequest {
|
|
8
|
+
content_type: string;
|
|
9
|
+
data_json: string;
|
|
10
|
+
config_json: string;
|
|
11
|
+
request_json: string;
|
|
12
|
+
event: number;
|
|
13
|
+
}
|
|
14
|
+
export interface CallAgentOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Google ID token (audience = the agent's Cloud Run URL) to get past Cloud
|
|
17
|
+
* Run's `--no-allow-unauthenticated` ingress. Attached as `authorization:
|
|
18
|
+
* Bearer` metadata. The agent never sees it; ingress consumes it.
|
|
19
|
+
*/
|
|
20
|
+
idToken?: string;
|
|
21
|
+
/** Called for each streamed `Progress` frame (e.g. to forward to a socket). */
|
|
22
|
+
onProgress?: (update: {
|
|
23
|
+
progress: number;
|
|
24
|
+
message: string;
|
|
25
|
+
}) => void;
|
|
26
|
+
/** Abort the RPC (e.g. the frontend socket dropped) → stops the agent's LLM. */
|
|
27
|
+
signal?: AbortSignal;
|
|
28
|
+
/** Call deadline; defaults to 900s to match Cloud Run's max request timeout. */
|
|
29
|
+
deadlineMs?: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Calls a deployed agent's `Agent.Execute` over gRPC server-streaming. Forwards
|
|
33
|
+
* each `Progress` frame to `onProgress`, resolves with the parsed `result_json`
|
|
34
|
+
* on `Result`, and rejects (with `error.code`) on `ErrorInfo`. Caller-cancel via
|
|
35
|
+
* `options.signal` cancels the RPC.
|
|
36
|
+
*
|
|
37
|
+
* @param agentUrl - The agent's Cloud Run URL (https://…run.app).
|
|
38
|
+
* @param request - The `ExecuteRequest` (its `request_json` carries `authToken`).
|
|
39
|
+
*/
|
|
40
|
+
export declare const callAgent: (agentUrl: string, request: AgentExecuteRequest, options?: CallAgentOptions) => Promise<any>;
|
|
41
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +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;AAqCD;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,GACpB,UAAU,MAAM,EAChB,SAAS,mBAAmB,EAC5B,UAAS,gBAAqB,KAC7B,OAAO,CAAC,GAAG,CAyDV,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as grpc from "@grpc/grpc-js";
|
|
2
|
+
import { loadAgentPackage } from "./agentProto.js";
|
|
3
|
+
const DEFAULT_DEADLINE_MS = 900_000;
|
|
4
|
+
const safeParse = (value, fallback) => {
|
|
5
|
+
if (!value)
|
|
6
|
+
return fallback;
|
|
7
|
+
try {
|
|
8
|
+
return JSON.parse(value);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return fallback;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
/** Turns an `ErrorInfo` frame into an Error carrying the agent's `code`. */
|
|
15
|
+
const agentError = (frame) => {
|
|
16
|
+
const error = new Error(frame?.message || "Agent error");
|
|
17
|
+
error.code = frame?.code;
|
|
18
|
+
return error;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Channel credentials for an authenticated Cloud Run agent: TLS (Cloud Run
|
|
22
|
+
* terminates at the edge) plus per-call metadata carrying the ID token.
|
|
23
|
+
*/
|
|
24
|
+
const buildCredentials = (idToken) => {
|
|
25
|
+
const ssl = grpc.credentials.createSsl();
|
|
26
|
+
if (!idToken) {
|
|
27
|
+
return ssl;
|
|
28
|
+
}
|
|
29
|
+
const callCreds = grpc.credentials.createFromMetadataGenerator((_ctx, cb) => {
|
|
30
|
+
const metadata = new grpc.Metadata();
|
|
31
|
+
metadata.add("authorization", `Bearer ${idToken}`);
|
|
32
|
+
cb(null, metadata);
|
|
33
|
+
});
|
|
34
|
+
return grpc.credentials.combineChannelCredentials(ssl, callCreds);
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Calls a deployed agent's `Agent.Execute` over gRPC server-streaming. Forwards
|
|
38
|
+
* each `Progress` frame to `onProgress`, resolves with the parsed `result_json`
|
|
39
|
+
* on `Result`, and rejects (with `error.code`) on `ErrorInfo`. Caller-cancel via
|
|
40
|
+
* `options.signal` cancels the RPC.
|
|
41
|
+
*
|
|
42
|
+
* @param agentUrl - The agent's Cloud Run URL (https://…run.app).
|
|
43
|
+
* @param request - The `ExecuteRequest` (its `request_json` carries `authToken`).
|
|
44
|
+
*/
|
|
45
|
+
export const callAgent = (agentUrl, request, options = {}) => new Promise((resolve, reject) => {
|
|
46
|
+
const loaded = loadAgentPackage();
|
|
47
|
+
const url = new URL(agentUrl);
|
|
48
|
+
const address = `${url.hostname}:${url.port || "443"}`;
|
|
49
|
+
const client = new loaded.stackfactor.agent.v1.Agent(address, buildCredentials(options.idToken));
|
|
50
|
+
const stream = client.Execute(request, {
|
|
51
|
+
deadline: Date.now() + (options.deadlineMs ?? DEFAULT_DEADLINE_MS),
|
|
52
|
+
});
|
|
53
|
+
let settled = false;
|
|
54
|
+
const settle = (run) => {
|
|
55
|
+
if (settled) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
settled = true;
|
|
59
|
+
try {
|
|
60
|
+
client.close();
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// ignore close errors
|
|
64
|
+
}
|
|
65
|
+
run();
|
|
66
|
+
};
|
|
67
|
+
if (options.signal) {
|
|
68
|
+
if (options.signal.aborted) {
|
|
69
|
+
stream.cancel();
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
options.signal.addEventListener("abort", () => stream.cancel(), {
|
|
73
|
+
once: true,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
stream.on("data", (update) => {
|
|
78
|
+
if (update.progress) {
|
|
79
|
+
options.onProgress?.({
|
|
80
|
+
progress: update.progress.progress ?? 0,
|
|
81
|
+
message: update.progress.message ?? "",
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
else if (update.result) {
|
|
85
|
+
const result = safeParse(update.result.result_json, null);
|
|
86
|
+
settle(() => resolve(result));
|
|
87
|
+
}
|
|
88
|
+
else if (update.error) {
|
|
89
|
+
settle(() => reject(agentError(update.error)));
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
stream.on("error", (error) => settle(() => reject(error)));
|
|
93
|
+
stream.on("end", () => settle(() => reject(new Error("Agent stream ended without a result or error"))));
|
|
94
|
+
});
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -3,11 +3,14 @@ import errorHandling from "./errorHandling.js";
|
|
|
3
3
|
import langChain from "./langChain.js";
|
|
4
4
|
import logger from "./logger.js";
|
|
5
5
|
import { serve } from "./serve.js";
|
|
6
|
+
import { callAgent } from "./client.js";
|
|
6
7
|
import * as runtimeContext from "./runtimeContext.js";
|
|
7
8
|
export { constants };
|
|
8
9
|
export { errorHandling };
|
|
9
10
|
export { langChain };
|
|
10
11
|
export { logger };
|
|
11
12
|
export { serve };
|
|
13
|
+
export { callAgent };
|
|
14
|
+
export type { AgentExecuteRequest, CallAgentOptions } from "./client.js";
|
|
12
15
|
export { runtimeContext };
|
|
13
16
|
//# sourceMappingURL=index.d.ts.map
|
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,MAAM,oBAAoB,CAAC;AAC/C,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,aAAa,EAAE,CAAC;AAEzB,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,MAAM,EAAE,CAAC;AAElB,OAAO,EAAE,KAAK,EAAE,CAAC;AAEjB,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,aAAa,MAAM,oBAAoB,CAAC;AAC/C,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,aAAa,EAAE,CAAC;AAEzB,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,MAAM,EAAE,CAAC;AAElB,OAAO,EAAE,KAAK,EAAE,CAAC;AAEjB,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEzE,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
package/dist/esm/index.js
CHANGED
|
@@ -3,10 +3,12 @@ import errorHandling from "./errorHandling.js";
|
|
|
3
3
|
import langChain from "./langChain.js";
|
|
4
4
|
import logger from "./logger.js";
|
|
5
5
|
import { serve } from "./serve.js";
|
|
6
|
+
import { callAgent } from "./client.js";
|
|
6
7
|
import * as runtimeContext from "./runtimeContext.js";
|
|
7
8
|
export { constants };
|
|
8
9
|
export { errorHandling };
|
|
9
10
|
export { langChain };
|
|
10
11
|
export { logger };
|
|
11
12
|
export { serve };
|
|
13
|
+
export { callAgent };
|
|
12
14
|
export { runtimeContext };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":";0CAiD6C,GAAG,KAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":";0CAiD6C,GAAG,KAAG,IAAI;wBAyhB/C,MAAM,aACD,MAAM,gBACH,MAAM,SACb,GAAG,EAAE,kBACI,GAAG,UACX,GAAG,KACV,GAAG;sBAyBG,GAAG,UACF,MAAM,UACN,GAAG,eACC,QAAQ,GAAG,IAAI,KAC1B,OAAO,CAAC,GAAG,CAAC;oCAqXF,MAAM,UACT,GAAG,UACH,GAAG,oBACO,GAAG,eACT,MAAM,eACN,MAAM,wBACG,OAAO,WACpB,GAAG,cACA,MAAM,UACV,GAAG,EAAE,KACX,OAAO,CAAC,GAAG,CAAC;sDAosBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,KACX,OAAO,CAAC,GAAG,CAAC;0CAn+B8B,GAAG,KAAG,MAAM;mCAoBxC,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AAwgCT,wBAQE"}
|
package/dist/esm/langChain.js
CHANGED
|
@@ -76,11 +76,11 @@ const getModelRate = (modelName, config, kind) => {
|
|
|
76
76
|
return Number.isFinite(rate) ? rate : null;
|
|
77
77
|
};
|
|
78
78
|
/**
|
|
79
|
-
* Accumulates the per-model
|
|
80
|
-
* agent can report input
|
|
81
|
-
* quota global is absent.
|
|
79
|
+
* Accumulates the per-model usage breakdown on `global.quota.byModel` so the
|
|
80
|
+
* agent can report input/output token counts and image spend per model to the
|
|
81
|
+
* UI. No-ops when the quota global is absent.
|
|
82
82
|
*/
|
|
83
|
-
const recordModelBreakdown = (modelName, inputCost, outputCost, imageCost) => {
|
|
83
|
+
const recordModelBreakdown = (modelName, inputCost, outputCost, imageCost, inputTokens = 0, outputTokens = 0) => {
|
|
84
84
|
const quota = globalThis.quota;
|
|
85
85
|
if (!quota || !modelName)
|
|
86
86
|
return;
|
|
@@ -90,13 +90,24 @@ const recordModelBreakdown = (modelName, inputCost, outputCost, imageCost) => {
|
|
|
90
90
|
inputCost: 0,
|
|
91
91
|
outputCost: 0,
|
|
92
92
|
imageCost: 0,
|
|
93
|
+
inputTokens: 0,
|
|
94
|
+
outputTokens: 0,
|
|
93
95
|
});
|
|
96
|
+
// Entries created by older versions of this module lack the token counters.
|
|
97
|
+
if (typeof entry.inputTokens !== "number")
|
|
98
|
+
entry.inputTokens = 0;
|
|
99
|
+
if (typeof entry.outputTokens !== "number")
|
|
100
|
+
entry.outputTokens = 0;
|
|
94
101
|
if (Number.isFinite(inputCost) && inputCost > 0)
|
|
95
102
|
entry.inputCost += inputCost;
|
|
96
103
|
if (Number.isFinite(outputCost) && outputCost > 0)
|
|
97
104
|
entry.outputCost += outputCost;
|
|
98
105
|
if (Number.isFinite(imageCost) && imageCost > 0)
|
|
99
106
|
entry.imageCost += imageCost;
|
|
107
|
+
if (Number.isFinite(inputTokens) && inputTokens > 0)
|
|
108
|
+
entry.inputTokens += inputTokens;
|
|
109
|
+
if (Number.isFinite(outputTokens) && outputTokens > 0)
|
|
110
|
+
entry.outputTokens += outputTokens;
|
|
100
111
|
};
|
|
101
112
|
/**
|
|
102
113
|
* Computes and records the USD cost of a text LLM call from the
|
|
@@ -110,10 +121,12 @@ const recordTextCost = (modelName, usage, config) => {
|
|
|
110
121
|
return;
|
|
111
122
|
const inputRate = getModelRate(modelName, config, "input-token") || 0;
|
|
112
123
|
const outputRate = getModelRate(modelName, config, "output-token") || 0;
|
|
113
|
-
const
|
|
114
|
-
const
|
|
124
|
+
const inputTokens = usage.input_tokens || 0;
|
|
125
|
+
const outputTokens = usage.output_tokens || 0;
|
|
126
|
+
const inputCost = (inputTokens / 1_000_000) * inputRate;
|
|
127
|
+
const outputCost = (outputTokens / 1_000_000) * outputRate;
|
|
115
128
|
recordCost(inputCost + outputCost);
|
|
116
|
-
recordModelBreakdown(modelName, inputCost, outputCost, 0);
|
|
129
|
+
recordModelBreakdown(modelName, inputCost, outputCost, 0, inputTokens, outputTokens);
|
|
117
130
|
};
|
|
118
131
|
/**
|
|
119
132
|
* Computes and records the USD cost of an image generation call from the
|
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;AAMtC;;;;;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,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC;IAAE,IAAI,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC,CAAC;AAElF,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;AAkDD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,GAAI,MAAM,SAAS,EAAE,UAAS,YAAiB,KAAG,IAAI,CAAC,MAsGxE,CAAC"}
|
package/dist/esm/serve.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import * as grpc from "@grpc/grpc-js";
|
|
2
|
-
import * as protoLoader from "@grpc/proto-loader";
|
|
3
2
|
import { session as clientSession } from "@stackfactor/client-api";
|
|
4
|
-
import { writeFileSync } from "node:fs";
|
|
5
|
-
import { join } from "node:path";
|
|
6
|
-
import { tmpdir } from "node:os";
|
|
7
3
|
import logger from "./logger.js";
|
|
4
|
+
import { loadAgentService } from "./agentProto.js";
|
|
8
5
|
import { runWithContext, installGlobals } from "./runtimeContext.js";
|
|
9
6
|
/** Marks an auth failure so the handler can map it to gRPC UNAUTHENTICATED. */
|
|
10
7
|
class UnauthenticatedError extends Error {
|
|
@@ -27,55 +24,6 @@ const defaultAuthenticate = async (request) => {
|
|
|
27
24
|
throw new UnauthenticatedError(`Invalid auth token: ${error?.message ?? error}`);
|
|
28
25
|
}
|
|
29
26
|
};
|
|
30
|
-
/**
|
|
31
|
-
* gRPC contract shared by the entire agent fleet. `data`/`config`/`request` are
|
|
32
|
-
* carried as JSON strings so arbitrary payloads need no proto schema churn.
|
|
33
|
-
* `Execute` is server-streaming: zero or more `Progress` frames (driven by the
|
|
34
|
-
* agent's `onProgress`) followed by exactly one `Result` or `Error`.
|
|
35
|
-
*/
|
|
36
|
-
const PROTO = `
|
|
37
|
-
syntax = "proto3";
|
|
38
|
-
package stackfactor.agent.v1;
|
|
39
|
-
|
|
40
|
-
service Agent {
|
|
41
|
-
rpc Execute(ExecuteRequest) returns (stream Update);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
message ExecuteRequest {
|
|
45
|
-
string content_type = 1;
|
|
46
|
-
string data_json = 2;
|
|
47
|
-
string config_json = 3;
|
|
48
|
-
string request_json = 4;
|
|
49
|
-
int32 event = 5;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
message Update {
|
|
53
|
-
oneof payload {
|
|
54
|
-
Progress progress = 1;
|
|
55
|
-
Result result = 2;
|
|
56
|
-
ErrorInfo error = 3;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
message Progress { int32 progress = 1; string message = 2; }
|
|
61
|
-
message Result { string result_json = 1; }
|
|
62
|
-
message ErrorInfo { int32 code = 1; string message = 2; }
|
|
63
|
-
`;
|
|
64
|
-
const loadAgentService = () => {
|
|
65
|
-
// proto-loader reads from a file; write the embedded schema to a temp path so
|
|
66
|
-
// the package stays self-contained across the cjs/esm dual build.
|
|
67
|
-
const file = join(tmpdir(), "stackfactor-agent.v1.proto");
|
|
68
|
-
writeFileSync(file, PROTO);
|
|
69
|
-
const packageDefinition = protoLoader.loadSync(file, {
|
|
70
|
-
keepCase: true,
|
|
71
|
-
longs: String,
|
|
72
|
-
enums: String,
|
|
73
|
-
defaults: true,
|
|
74
|
-
oneofs: true,
|
|
75
|
-
});
|
|
76
|
-
const loaded = grpc.loadPackageDefinition(packageDefinition);
|
|
77
|
-
return loaded.stackfactor.agent.v1.Agent.service;
|
|
78
|
-
};
|
|
79
27
|
const safeParse = (value, fallback) => {
|
|
80
28
|
if (!value)
|
|
81
29
|
return fallback;
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackfactor/agent-utils",
|
|
3
3
|
"publishConfig": {
|
|
4
|
-
"access": "
|
|
4
|
+
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.1.
|
|
6
|
+
"version": "1.1.3",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "dist/cjs/index.js",
|
|
9
9
|
"module": "dist/esm/index.js",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@langchain/google-genai": "^2.1.26",
|
|
47
47
|
"@langchain/openai": "^1.3.0",
|
|
48
48
|
"@opentelemetry/api": "^1.9.0",
|
|
49
|
-
"@stackfactor/client-api": "^1.1.
|
|
49
|
+
"@stackfactor/client-api": "^1.1.241",
|
|
50
50
|
"langchain": "^1.2.35",
|
|
51
51
|
"mongoose": "^9.3.1",
|
|
52
52
|
"openai": "^6.32.0",
|