@stackfactor/agent-utils 1.1.1 → 1.1.2

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.
@@ -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;
@@ -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
@@ -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;wBAqgB/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"}
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"}
@@ -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 cost breakdown on `global.quota.byModel` so the
85
- * agent can report input vs output spend per model to the UI. No-ops when the
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 inputCost = ((usage.input_tokens || 0) / 1_000_000) * inputRate;
119
- const outputCost = ((usage.output_tokens || 0) / 1_000_000) * outputRate;
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
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,eAAe,CAAC;AAStC;;;;;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;AAqGD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,GAAI,MAAM,SAAS,EAAE,UAAS,YAAiB,KAAG,IAAI,CAAC,MAsGxE,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
+ });
@@ -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
@@ -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;wBAqgB/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"}
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"}
@@ -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 cost breakdown on `global.quota.byModel` so the
80
- * agent can report input vs output spend per model to the UI. No-ops when the
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 inputCost = ((usage.input_tokens || 0) / 1_000_000) * inputRate;
114
- const outputCost = ((usage.output_tokens || 0) / 1_000_000) * outputRate;
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
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,eAAe,CAAC;AAStC;;;;;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;AAqGD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,GAAI,MAAM,SAAS,EAAE,UAAS,YAAiB,KAAG,IAAI,CAAC,MAsGxE,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": "restricted"
4
+ "access": "public"
5
5
  },
6
- "version": "1.1.1",
6
+ "version": "1.1.2",
7
7
  "description": "",
8
8
  "main": "dist/cjs/index.js",
9
9
  "module": "dist/esm/index.js",