fg-tracker-sdk 0.11.0 → 0.14.0
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/CHANGELOG.md +34 -0
- package/README.md +88 -34
- package/dist/context.d.ts +23 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +29 -5
- package/dist/context.js.map +1 -1
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +3 -1
- package/dist/database.js.map +1 -1
- package/dist/express.js +1 -1
- package/dist/express.js.map +1 -1
- package/dist/fetch.d.ts.map +1 -1
- package/dist/fetch.js +31 -2
- package/dist/fetch.js.map +1 -1
- package/dist/genai/bedrock.d.ts +22 -0
- package/dist/genai/bedrock.d.ts.map +1 -0
- package/dist/genai/bedrock.js +161 -0
- package/dist/genai/bedrock.js.map +1 -0
- package/dist/genai/call.d.ts +96 -0
- package/dist/genai/call.d.ts.map +1 -0
- package/dist/genai/call.js +260 -0
- package/dist/genai/call.js.map +1 -0
- package/dist/genai/config.d.ts +71 -0
- package/dist/genai/config.d.ts.map +1 -0
- package/dist/genai/config.js +110 -0
- package/dist/genai/config.js.map +1 -0
- package/dist/genai/http.d.ts +49 -0
- package/dist/genai/http.d.ts.map +1 -0
- package/dist/genai/http.js +795 -0
- package/dist/genai/http.js.map +1 -0
- package/dist/genai/semconv.d.ts +64 -0
- package/dist/genai/semconv.d.ts.map +1 -0
- package/dist/genai/semconv.js +52 -0
- package/dist/genai/semconv.js.map +1 -0
- package/dist/genai/sse.d.ts +26 -0
- package/dist/genai/sse.d.ts.map +1 -0
- package/dist/genai/sse.js +100 -0
- package/dist/genai/sse.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/infra/aws.d.ts +7 -4
- package/dist/infra/aws.d.ts.map +1 -1
- package/dist/infra/aws.js +50 -9
- package/dist/infra/aws.js.map +1 -1
- package/dist/infra/lambda.js +1 -1
- package/dist/infra/lambda.js.map +1 -1
- package/dist/infra/messaging.d.ts +10 -2
- package/dist/infra/messaging.d.ts.map +1 -1
- package/dist/infra/messaging.js +22 -3
- package/dist/infra/messaging.js.map +1 -1
- package/dist/next.js +1 -1
- package/dist/next.js.map +1 -1
- package/dist/normalize.d.ts +58 -8
- package/dist/normalize.d.ts.map +1 -1
- package/dist/normalize.js +383 -79
- package/dist/normalize.js.map +1 -1
- package/dist/pg.d.ts +12 -2
- package/dist/pg.d.ts.map +1 -1
- package/dist/pg.js +464 -213
- package/dist/pg.js.map +1 -1
- package/dist/tracker.d.ts +36 -5
- package/dist/tracker.d.ts.map +1 -1
- package/dist/tracker.js +55 -6
- package/dist/tracker.js.map +1 -1
- package/dist/types.d.ts +27 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -1
package/dist/fetch.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { performance } from 'node:perf_hooks';
|
|
1
2
|
import { TRACEPARENT_HEADER, TRACESTATE_HEADER, traceContextHeaders } from './context.js';
|
|
3
|
+
import { matchGenAiTarget } from './genai/config.js';
|
|
4
|
+
import { failHttpGenAiCall, observeHttpGenAiResponse, startHttpGenAiCall } from './genai/http.js';
|
|
2
5
|
import { httpClientSemconv, normalizePath } from './normalize.js';
|
|
3
6
|
import { shouldPropagate } from './tracker.js';
|
|
4
7
|
function describe(input, init) {
|
|
@@ -59,27 +62,53 @@ export function createInstrumentedFetch(core, fetchImpl = globalThis.fetch) {
|
|
|
59
62
|
if (!url || raw.startsWith(ownEndpoint))
|
|
60
63
|
return base(input, init);
|
|
61
64
|
const name = `${method} ${url.origin}${normalizePath(url.pathname)}`;
|
|
65
|
+
const startedPerf = performance.now();
|
|
62
66
|
const span = core.startSpan('http.client', name, { url: url.origin + url.pathname + url.search, host: url.host, ...httpClientSemconv(url) });
|
|
63
67
|
span.method = method;
|
|
64
68
|
span.route = normalizePath(url.pathname);
|
|
69
|
+
// Chamada de IA reconhecida: atributos `gen_ai.*` neste mesmo span, que fecha no fim da resposta (§30).
|
|
70
|
+
const genAiConfig = core.genAiFor(span);
|
|
71
|
+
const target = genAiConfig ? matchGenAiTarget(genAiConfig, method, url) : null;
|
|
72
|
+
let genAi = null;
|
|
73
|
+
if (genAiConfig && target) {
|
|
74
|
+
try {
|
|
75
|
+
genAi = startHttpGenAiCall(genAiConfig, span, target, input, init, startedPerf);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
genAi = null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
65
81
|
let request = { input, init };
|
|
66
82
|
if (shouldPropagate(core.options.propagateTraceTo, url)) {
|
|
67
83
|
request = withTraceContext(input, init, traceContextHeaders(span.context()));
|
|
68
84
|
span.setAttribute('propagated', true);
|
|
69
85
|
}
|
|
86
|
+
let handedOff = false;
|
|
87
|
+
const signal = init?.signal ?? (input instanceof Request ? input.signal : undefined);
|
|
70
88
|
try {
|
|
71
89
|
const res = await core.runInSpan(span, () => base(request.input, request.init));
|
|
72
90
|
span.statusCode = res.status;
|
|
73
91
|
if (res.status >= 500)
|
|
74
92
|
span.error = { name: 'HttpError', message: `HTTP ${res.status}` };
|
|
93
|
+
if (genAi && target) {
|
|
94
|
+
handedOff = true;
|
|
95
|
+
return observeHttpGenAiResponse(genAi, res, target.api, signal);
|
|
96
|
+
}
|
|
75
97
|
return res;
|
|
76
98
|
}
|
|
77
99
|
catch (err) {
|
|
78
|
-
|
|
100
|
+
if (genAi && !handedOff) {
|
|
101
|
+
handedOff = true;
|
|
102
|
+
failHttpGenAiCall(genAi, err, signal);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
span.setError(err);
|
|
106
|
+
}
|
|
79
107
|
throw err;
|
|
80
108
|
}
|
|
81
109
|
finally {
|
|
82
|
-
|
|
110
|
+
if (!handedOff)
|
|
111
|
+
span.end();
|
|
83
112
|
}
|
|
84
113
|
};
|
|
85
114
|
}
|
package/dist/fetch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.js","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"fetch.js","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAE1F,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,eAAe,EAAoB,MAAM,cAAc,CAAC;AAEjE,SAAS,QAAQ,CAAC,KAA6B,EAAE,IAAkB;IACjE,IAAI,GAAW,CAAC;IAChB,IAAI,MAAM,GAAG,IAAI,EAAE,MAAM,CAAC;IAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,GAAG,GAAG,KAAK,CAAC;SACtC,IAAI,KAAK,YAAY,GAAG;QAAE,GAAG,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;SACjD,CAAC;QACJ,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;QAChB,MAAM,GAAG,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC;IAClC,CAAC;IACD,IAAI,GAAG,GAAe,IAAI,CAAC;IAC3B,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,GAAG,IAAI,CAAC;IACb,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,KAA6B,EAAE,IAA6B,EAAE,MAA8B;IACpH,MAAM,KAAK,GAAG,CAAC,OAAgB,EAAE,EAAE;QACjC,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,CAAE,CAAC,CAAC;QAC7D,IAAI,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAC9H,CAAC,CAAC;IACF,IAAI,KAAK,YAAY,OAAO,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC;QAC/C,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAClE,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC3C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,OAAO,EAAE,KAAK,EAAE,IAAI,OAAO,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC;IAC1D,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,KAAK,YAAY,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IACrG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;QAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACrD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC;AAC/C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAiB,EAAE,YAAqC,UAAU,CAAC,KAAK;IAC9G,MAAM,IAAI,GAAG,SAAS,CAAC;IACvB,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC1C,OAAO,KAAK,UAAU,cAAc,CAAC,KAA6B,EAAE,IAAkB;QACpF,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE;YAAE,OAAO,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACtE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACnD,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,WAAW,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAElE,MAAM,IAAI,GAAG,GAAG,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrE,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7I,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACzC,wGAAwG;QACxG,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/E,IAAI,KAAK,GAAqB,IAAI,CAAC;QACnC,IAAI,WAAW,IAAI,MAAM,EAAE,CAAC;YAC1B,IAAI,CAAC;gBACH,KAAK,GAAG,kBAAkB,CAAC,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;YAClF,CAAC;YAAC,MAAM,CAAC;gBACP,KAAK,GAAG,IAAI,CAAC;YACf,CAAC;QACH,CAAC;QACD,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAC9B,IAAI,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,EAAE,CAAC;YACxD,OAAO,GAAG,gBAAgB,CAAC,KAAK,EAAE,IAAI,EAAE,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC7E,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,IAAI,CAAC,KAAK,YAAY,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACrF,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;YAChF,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC;YAC7B,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG;gBAAE,IAAI,CAAC,KAAK,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;YACzF,IAAI,KAAK,IAAI,MAAM,EAAE,CAAC;gBACpB,SAAS,GAAG,IAAI,CAAC;gBACjB,OAAO,wBAAwB,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAClE,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;gBACxB,SAAS,GAAG,IAAI,CAAC;gBACjB,iBAAiB,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;YACxC,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YACrB,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,SAAS;gBAAE,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ActiveSpan } from '../types.js';
|
|
2
|
+
import { type GenAiUsage } from './call.js';
|
|
3
|
+
import type { ResolvedGenAi } from './config.js';
|
|
4
|
+
/**
|
|
5
|
+
* Ganchos que o middleware AWS chama em volta de `next()`. Com observador, o fim do span é dele:
|
|
6
|
+
* na hora (`Converse`, erro) ou quando o stream de eventos termina (`ConverseStream`).
|
|
7
|
+
*/
|
|
8
|
+
export interface AwsGenAiObserver {
|
|
9
|
+
output(result: {
|
|
10
|
+
output?: unknown;
|
|
11
|
+
} | undefined): void;
|
|
12
|
+
/** Cancelamento fecha sem `span.error`; o resto, com o erro. */
|
|
13
|
+
error(err: unknown): void;
|
|
14
|
+
}
|
|
15
|
+
export declare function isBedrockRuntime(label: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* `usage` do Converse. A AWS documenta `inputTokens` sem o cache (`totalTokens` = entrada + saída +
|
|
18
|
+
* cache); se o próprio `totalTokens` mostrar que o cache já está dentro, não soma de novo.
|
|
19
|
+
*/
|
|
20
|
+
export declare function bedrockUsage(raw: unknown): GenAiUsage;
|
|
21
|
+
export declare function startBedrockCall(config: ResolvedGenAi, span: ActiveSpan, operation: string, input: unknown, startedPerf?: number): AwsGenAiObserver | null;
|
|
22
|
+
//# sourceMappingURL=bedrock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bedrock.d.ts","sourceRoot":"","sources":["../../src/genai/bedrock.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAiD,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAC3F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAgBjD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS,GAAG,IAAI,CAAC;IACvD,gEAAgE;IAChE,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;CAC3B;AAID,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAerD;AAeD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,GAAE,MAA0B,GAAG,gBAAgB,GAAG,IAAI,CA+C7K"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { performance } from 'node:perf_hooks';
|
|
2
|
+
import { errorTypeOf, GenAiCall, modelName, tokenCount } from './call.js';
|
|
3
|
+
import { isAbortError } from './http.js';
|
|
4
|
+
import { GEN_AI } from './semconv.js';
|
|
5
|
+
function obj(value) {
|
|
6
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
7
|
+
}
|
|
8
|
+
const OPERATIONS = new Set(['Converse', 'ConverseStream']);
|
|
9
|
+
export function isBedrockRuntime(label) {
|
|
10
|
+
return label.toLowerCase().replace(/[^a-z0-9]/g, '') === 'bedrockruntime';
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* `usage` do Converse. A AWS documenta `inputTokens` sem o cache (`totalTokens` = entrada + saída +
|
|
14
|
+
* cache); se o próprio `totalTokens` mostrar que o cache já está dentro, não soma de novo.
|
|
15
|
+
*/
|
|
16
|
+
export function bedrockUsage(raw) {
|
|
17
|
+
const usage = obj(raw);
|
|
18
|
+
const input = tokenCount(usage.inputTokens);
|
|
19
|
+
const output = tokenCount(usage.outputTokens);
|
|
20
|
+
const total = tokenCount(usage.totalTokens);
|
|
21
|
+
const cacheRead = tokenCount(usage.cacheReadInputTokens);
|
|
22
|
+
const cacheCreation = tokenCount(usage.cacheWriteInputTokens);
|
|
23
|
+
const cache = (cacheRead ?? 0) + (cacheCreation ?? 0);
|
|
24
|
+
const alreadyIncluded = cache > 0 && input !== undefined && output !== undefined && total === input + output;
|
|
25
|
+
return {
|
|
26
|
+
input: input === undefined ? undefined : alreadyIncluded ? input : input + cache,
|
|
27
|
+
output,
|
|
28
|
+
cacheRead,
|
|
29
|
+
cacheCreation,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function bedrockErrorType(err) {
|
|
33
|
+
const named = err;
|
|
34
|
+
return errorTypeOf(named?.name) ?? errorTypeOf(named?.$metadata?.httpStatusCode) ?? '_OTHER';
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Erro da chamada sem a mensagem do serviço: num `ValidationException` ou erro do modelo ela pode
|
|
38
|
+
* repetir trechos do pedido. Fica o tipo (que também agrupa as issues).
|
|
39
|
+
*/
|
|
40
|
+
function sanitizedError(type) {
|
|
41
|
+
return { name: type, message: `aws.bedrock: ${type}` };
|
|
42
|
+
}
|
|
43
|
+
export function startBedrockCall(config, span, operation, input, startedPerf = performance.now()) {
|
|
44
|
+
if (!OPERATIONS.has(operation))
|
|
45
|
+
return null;
|
|
46
|
+
const request = obj(input);
|
|
47
|
+
const call = new GenAiCall(span, config, 'aws.bedrock', 'chat', startedPerf, modelName(request.modelId));
|
|
48
|
+
const stream = operation === 'ConverseStream';
|
|
49
|
+
if (stream)
|
|
50
|
+
call.setRequest(GEN_AI.requestStream, true);
|
|
51
|
+
const inference = obj(request.inferenceConfig);
|
|
52
|
+
const maxTokens = inference.maxTokens;
|
|
53
|
+
if (typeof maxTokens === 'number' && Number.isSafeInteger(maxTokens))
|
|
54
|
+
call.setRequest(GEN_AI.requestMaxTokens, maxTokens);
|
|
55
|
+
for (const [key, attribute] of [
|
|
56
|
+
['temperature', GEN_AI.requestTemperature],
|
|
57
|
+
['topP', GEN_AI.requestTopP],
|
|
58
|
+
]) {
|
|
59
|
+
const value = inference[key];
|
|
60
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
61
|
+
call.setRequest(attribute, value);
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
output(result) {
|
|
65
|
+
const output = obj(result?.output);
|
|
66
|
+
try {
|
|
67
|
+
if (!stream) {
|
|
68
|
+
call.setFinishReason(0, output.stopReason);
|
|
69
|
+
call.usage = bedrockUsage(output.usage);
|
|
70
|
+
call.finish('completed');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const events = output.stream;
|
|
74
|
+
if (!events || typeof events[Symbol.asyncIterator] !== 'function') {
|
|
75
|
+
call.finish('unobserved');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
call.armWatchdog();
|
|
79
|
+
output.stream = observeEvents(call, events);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
call.finish('unobserved');
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
error(err) {
|
|
86
|
+
if (isAbortError(err)) {
|
|
87
|
+
call.finish('cancelled');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
call.errorType = bedrockErrorType(err);
|
|
91
|
+
call.finish('error', sanitizedError(call.errorType));
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/** Eventos do `ConverseStream` passam intactos; `metadata` traz o uso, `messageStop` o motivo. */
|
|
96
|
+
function observeEvents(call, events) {
|
|
97
|
+
let finalUsage = false;
|
|
98
|
+
let stopped = false;
|
|
99
|
+
const observe = (event) => {
|
|
100
|
+
if (call.done)
|
|
101
|
+
return;
|
|
102
|
+
try {
|
|
103
|
+
call.markActivity();
|
|
104
|
+
call.markFirstChunk();
|
|
105
|
+
const item = obj(event);
|
|
106
|
+
if (item.messageStop) {
|
|
107
|
+
stopped = true;
|
|
108
|
+
call.setFinishReason(0, obj(item.messageStop).stopReason);
|
|
109
|
+
}
|
|
110
|
+
if (item.metadata) {
|
|
111
|
+
call.usage = bedrockUsage(obj(item.metadata).usage);
|
|
112
|
+
finalUsage = call.usage.input !== undefined || call.usage.output !== undefined;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
call.finish('unobserved');
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const outcome = () => (finalUsage || stopped ? 'completed' : 'incomplete');
|
|
120
|
+
return {
|
|
121
|
+
[Symbol.asyncIterator]() {
|
|
122
|
+
const iterator = events[Symbol.asyncIterator]();
|
|
123
|
+
return {
|
|
124
|
+
async next(...args) {
|
|
125
|
+
let result;
|
|
126
|
+
try {
|
|
127
|
+
result = await iterator.next(...args);
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
if (isAbortError(err))
|
|
131
|
+
call.finish('cancelled');
|
|
132
|
+
else {
|
|
133
|
+
call.errorType = bedrockErrorType(err);
|
|
134
|
+
call.finish('error', sanitizedError(call.errorType));
|
|
135
|
+
}
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
if (result.done)
|
|
139
|
+
call.finish(outcome());
|
|
140
|
+
else
|
|
141
|
+
observe(result.value);
|
|
142
|
+
return result;
|
|
143
|
+
},
|
|
144
|
+
async return(value) {
|
|
145
|
+
// `break` no `for await`: quem consumia desistiu; o SDK fecha a conexão.
|
|
146
|
+
if (!call.done)
|
|
147
|
+
call.finish(finalUsage ? 'completed' : 'cancelled');
|
|
148
|
+
return iterator.return ? iterator.return(value) : { done: true, value };
|
|
149
|
+
},
|
|
150
|
+
async throw(err) {
|
|
151
|
+
if (!call.done)
|
|
152
|
+
call.finish('cancelled');
|
|
153
|
+
if (iterator.throw)
|
|
154
|
+
return iterator.throw(err);
|
|
155
|
+
throw err;
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=bedrock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bedrock.js","sourceRoot":"","sources":["../../src/genai/bedrock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { ActiveSpan } from '../types.js';
|
|
2
|
+
import type { GenAiPricing, ResolvedGenAi } from './config.js';
|
|
3
|
+
import { type GenAiOperation, type GenAiOutcome } from './semconv.js';
|
|
4
|
+
/**
|
|
5
|
+
* Uso informado pelo provedor, já na semântica do OpenTelemetry: `input` inclui os tokens de cache
|
|
6
|
+
* (lidos e gravados) e `output` inclui os de raciocínio. Campo ausente = o provedor não informou.
|
|
7
|
+
*/
|
|
8
|
+
export interface GenAiUsage {
|
|
9
|
+
input?: number;
|
|
10
|
+
output?: number;
|
|
11
|
+
cacheRead?: number;
|
|
12
|
+
cacheCreation?: number;
|
|
13
|
+
reasoning?: number;
|
|
14
|
+
}
|
|
15
|
+
/** Custo informado pelo próprio provedor (nunca estimado). */
|
|
16
|
+
export interface ReportedCost {
|
|
17
|
+
amount: number;
|
|
18
|
+
currency: string;
|
|
19
|
+
source: string;
|
|
20
|
+
}
|
|
21
|
+
/** Inteiro ≥ 0 informado pelo provedor; qualquer outra coisa conta como "não informado". */
|
|
22
|
+
export declare function tokenCount(value: unknown): number | undefined;
|
|
23
|
+
/** Identificador de erro de baixa cardinalidade (`rate_limit_error`, `429`); texto livre fica de fora. */
|
|
24
|
+
export declare function errorTypeOf(value: unknown): string | undefined;
|
|
25
|
+
/** `gen_ai.request.model`/`response.model`: nome de modelo, sem aceitar texto arbitrário enorme. */
|
|
26
|
+
export declare function modelName(value: unknown): string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Custo estimado pela tabela, ou `undefined` quando faltar qualquer dado: uso parcial ou ausente,
|
|
29
|
+
* classe de token sem preço, modelo fora da tabela. Nunca completa o que o provedor não informou.
|
|
30
|
+
*/
|
|
31
|
+
export declare function estimateCost(pricing: GenAiPricing, provider: string, operation: string, models: (string | undefined)[], usage: GenAiUsage): number | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Estado de uma chamada de IA presa a um span já existente (o `http.client` do `fetch` ou o span da
|
|
34
|
+
* chamada AWS). Não cria span: só acrescenta atributos `gen_ai.*`/`fg.gen_ai.*` e encerra o span
|
|
35
|
+
* uma única vez, quando a resposta termina (no stream, quando o corpo acaba ou é cancelado).
|
|
36
|
+
*/
|
|
37
|
+
export declare class GenAiCall {
|
|
38
|
+
readonly span: ActiveSpan;
|
|
39
|
+
private readonly config;
|
|
40
|
+
readonly provider: string;
|
|
41
|
+
readonly operation: GenAiOperation;
|
|
42
|
+
private readonly startedPerf;
|
|
43
|
+
readonly requestModel: string | undefined;
|
|
44
|
+
usage: GenAiUsage;
|
|
45
|
+
/** O stream terminou antes do uso final: o que há de uso é parcial (ex.: só a entrada). */
|
|
46
|
+
usagePartial: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Escolhas pedidas (`n` do Chat Completions; 1 sem o parâmetro). Um stream que acaba sem o marcador
|
|
49
|
+
* final só é `completed` se todas elas trouxeram motivo de parada.
|
|
50
|
+
*/
|
|
51
|
+
expectedChoices: number;
|
|
52
|
+
/**
|
|
53
|
+
* A resposta já chegou com HTTP ≥ 400: o desfecho é `error` de qualquer jeito. Prazo sem leitura,
|
|
54
|
+
* abort ou cancelamento do corpo depois dos headers não o trocam por `unobserved`/`cancelled`.
|
|
55
|
+
*/
|
|
56
|
+
failed: boolean;
|
|
57
|
+
reportedCost: ReportedCost | undefined;
|
|
58
|
+
responseId: string | undefined;
|
|
59
|
+
responseModel: string | undefined;
|
|
60
|
+
/** Motivo de parada por índice de escolha. */
|
|
61
|
+
readonly finishReasons: (string | undefined)[];
|
|
62
|
+
errorType: string | undefined;
|
|
63
|
+
private firstChunkPerf;
|
|
64
|
+
private finished;
|
|
65
|
+
private readonly cleanups;
|
|
66
|
+
private watchdog;
|
|
67
|
+
/** Última leitura do corpo (`performance.now()`): o prazo de observação conta a partir dela. */
|
|
68
|
+
private lastActivity;
|
|
69
|
+
constructor(span: ActiveSpan, config: ResolvedGenAi, provider: string, operation: GenAiOperation, startedPerf: number, requestModel: string | undefined);
|
|
70
|
+
get done(): boolean;
|
|
71
|
+
/** Atributo do pedido (só valores numéricos/booleanos/enumerados; nunca conteúdo). */
|
|
72
|
+
setRequest(key: string, value: unknown): void;
|
|
73
|
+
/** Primeiro evento do stream (`gen_ai.response.time_to_first_chunk`). */
|
|
74
|
+
markFirstChunk(): void;
|
|
75
|
+
setFinishReason(index: number, reason: unknown): void;
|
|
76
|
+
setResponseId(value: unknown): void;
|
|
77
|
+
setResponseModel(value: unknown): void;
|
|
78
|
+
/**
|
|
79
|
+
* Fecha a observação depois de `observationIdleMs` sem leitura do corpo (resposta abandonada): o
|
|
80
|
+
* span sai com o que foi visto. Um stream longo que continua sendo lido não é cortado.
|
|
81
|
+
*/
|
|
82
|
+
armWatchdog(): void;
|
|
83
|
+
/** Houve leitura do corpo: adia o prazo sem custo de timer por pedaço. */
|
|
84
|
+
markActivity(): void;
|
|
85
|
+
private schedule;
|
|
86
|
+
onFinish(cleanup: () => void): void;
|
|
87
|
+
/**
|
|
88
|
+
* Encerra a chamada. `error` só vira `span.error` quando informado (falha de rede, erro do provedor
|
|
89
|
+
* no corpo); HTTP 4xx fica em `error.type` sem virar issue, como no resto do `http.client`.
|
|
90
|
+
*/
|
|
91
|
+
finish(outcome: GenAiOutcome, error?: unknown): void;
|
|
92
|
+
private applyAttributes;
|
|
93
|
+
}
|
|
94
|
+
/** Custo informado pelo provedor no corpo (`usage.cost` do OpenRouter, em créditos = USD). */
|
|
95
|
+
export declare function reportedCost(provider: string, usage: Record<string, unknown>): ReportedCost | undefined;
|
|
96
|
+
//# sourceMappingURL=call.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call.d.ts","sourceRoot":"","sources":["../../src/genai/call.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { performance } from 'node:perf_hooks';
|
|
2
|
+
import { FG_GEN_AI, GEN_AI, GEN_AI_SEMCONV_VERSION } from './semconv.js';
|
|
3
|
+
/** Inteiro ≥ 0 informado pelo provedor; qualquer outra coisa conta como "não informado". */
|
|
4
|
+
export function tokenCount(value) {
|
|
5
|
+
return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : undefined;
|
|
6
|
+
}
|
|
7
|
+
/** Identificador de erro de baixa cardinalidade (`rate_limit_error`, `429`); texto livre fica de fora. */
|
|
8
|
+
export function errorTypeOf(value) {
|
|
9
|
+
if (typeof value === 'number' && Number.isInteger(value))
|
|
10
|
+
return String(value);
|
|
11
|
+
return typeof value === 'string' && /^[A-Za-z0-9_.:-]{1,64}$/.test(value) ? value : undefined;
|
|
12
|
+
}
|
|
13
|
+
/** `gen_ai.request.model`/`response.model`: nome de modelo, sem aceitar texto arbitrário enorme. */
|
|
14
|
+
export function modelName(value) {
|
|
15
|
+
return typeof value === 'string' && value.length > 0 && value.length <= 256 ? value : undefined;
|
|
16
|
+
}
|
|
17
|
+
function finiteNumber(value) {
|
|
18
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
19
|
+
}
|
|
20
|
+
function priceFor(pricing, provider, models) {
|
|
21
|
+
for (const model of models) {
|
|
22
|
+
if (!model)
|
|
23
|
+
continue;
|
|
24
|
+
const candidates = pricing.models.filter((entry) => entry.model === model && (entry.provider === undefined || entry.provider === provider));
|
|
25
|
+
// Entrada do provedor vence a genérica para o mesmo nome.
|
|
26
|
+
const best = candidates.find((entry) => entry.provider === provider) ?? candidates[0];
|
|
27
|
+
if (best)
|
|
28
|
+
return best;
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Custo estimado pela tabela, ou `undefined` quando faltar qualquer dado: uso parcial ou ausente,
|
|
34
|
+
* classe de token sem preço, modelo fora da tabela. Nunca completa o que o provedor não informou.
|
|
35
|
+
*/
|
|
36
|
+
export function estimateCost(pricing, provider, operation, models, usage) {
|
|
37
|
+
const price = priceFor(pricing, provider, models);
|
|
38
|
+
if (!price || usage.input === undefined)
|
|
39
|
+
return undefined;
|
|
40
|
+
const cacheRead = usage.cacheRead ?? 0;
|
|
41
|
+
const cacheCreation = usage.cacheCreation ?? 0;
|
|
42
|
+
const uncached = usage.input - cacheRead - cacheCreation;
|
|
43
|
+
if (uncached < 0)
|
|
44
|
+
return undefined;
|
|
45
|
+
if (cacheRead > 0 && price.cacheReadPerMillion === undefined)
|
|
46
|
+
return undefined;
|
|
47
|
+
if (cacheCreation > 0 && price.cacheCreationPerMillion === undefined)
|
|
48
|
+
return undefined;
|
|
49
|
+
let output = 0;
|
|
50
|
+
if (operation !== 'embeddings') {
|
|
51
|
+
if (usage.output === undefined || price.outputPerMillion === undefined)
|
|
52
|
+
return undefined;
|
|
53
|
+
output = usage.output * price.outputPerMillion;
|
|
54
|
+
}
|
|
55
|
+
else if (usage.output !== undefined && usage.output > 0) {
|
|
56
|
+
if (price.outputPerMillion === undefined)
|
|
57
|
+
return undefined;
|
|
58
|
+
output = usage.output * price.outputPerMillion;
|
|
59
|
+
}
|
|
60
|
+
const total = uncached * price.inputPerMillion + cacheRead * (price.cacheReadPerMillion ?? 0) + cacheCreation * (price.cacheCreationPerMillion ?? 0) + output;
|
|
61
|
+
return Math.round((total / 1_000_000) * 1e12) / 1e12;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Estado de uma chamada de IA presa a um span já existente (o `http.client` do `fetch` ou o span da
|
|
65
|
+
* chamada AWS). Não cria span: só acrescenta atributos `gen_ai.*`/`fg.gen_ai.*` e encerra o span
|
|
66
|
+
* uma única vez, quando a resposta termina (no stream, quando o corpo acaba ou é cancelado).
|
|
67
|
+
*/
|
|
68
|
+
export class GenAiCall {
|
|
69
|
+
span;
|
|
70
|
+
config;
|
|
71
|
+
provider;
|
|
72
|
+
operation;
|
|
73
|
+
startedPerf;
|
|
74
|
+
requestModel;
|
|
75
|
+
usage = {};
|
|
76
|
+
/** O stream terminou antes do uso final: o que há de uso é parcial (ex.: só a entrada). */
|
|
77
|
+
usagePartial = false;
|
|
78
|
+
/**
|
|
79
|
+
* Escolhas pedidas (`n` do Chat Completions; 1 sem o parâmetro). Um stream que acaba sem o marcador
|
|
80
|
+
* final só é `completed` se todas elas trouxeram motivo de parada.
|
|
81
|
+
*/
|
|
82
|
+
expectedChoices = 1;
|
|
83
|
+
/**
|
|
84
|
+
* A resposta já chegou com HTTP ≥ 400: o desfecho é `error` de qualquer jeito. Prazo sem leitura,
|
|
85
|
+
* abort ou cancelamento do corpo depois dos headers não o trocam por `unobserved`/`cancelled`.
|
|
86
|
+
*/
|
|
87
|
+
failed = false;
|
|
88
|
+
reportedCost;
|
|
89
|
+
responseId;
|
|
90
|
+
responseModel;
|
|
91
|
+
/** Motivo de parada por índice de escolha. */
|
|
92
|
+
finishReasons = [];
|
|
93
|
+
errorType;
|
|
94
|
+
firstChunkPerf;
|
|
95
|
+
finished = false;
|
|
96
|
+
cleanups = [];
|
|
97
|
+
watchdog;
|
|
98
|
+
/** Última leitura do corpo (`performance.now()`): o prazo de observação conta a partir dela. */
|
|
99
|
+
lastActivity = 0;
|
|
100
|
+
constructor(span, config, provider, operation, startedPerf, requestModel) {
|
|
101
|
+
this.span = span;
|
|
102
|
+
this.config = config;
|
|
103
|
+
this.provider = provider;
|
|
104
|
+
this.operation = operation;
|
|
105
|
+
this.startedPerf = startedPerf;
|
|
106
|
+
this.requestModel = requestModel;
|
|
107
|
+
span.setAttribute(GEN_AI.operation, operation);
|
|
108
|
+
span.setAttribute(GEN_AI.provider, provider);
|
|
109
|
+
span.setAttribute(FG_GEN_AI.semconvVersion, GEN_AI_SEMCONV_VERSION);
|
|
110
|
+
if (requestModel)
|
|
111
|
+
span.setAttribute(GEN_AI.requestModel, requestModel);
|
|
112
|
+
}
|
|
113
|
+
get done() {
|
|
114
|
+
return this.finished;
|
|
115
|
+
}
|
|
116
|
+
/** Atributo do pedido (só valores numéricos/booleanos/enumerados; nunca conteúdo). */
|
|
117
|
+
setRequest(key, value) {
|
|
118
|
+
if (value === undefined || value === null)
|
|
119
|
+
return;
|
|
120
|
+
this.span.setAttribute(key, value);
|
|
121
|
+
}
|
|
122
|
+
/** Primeiro evento do stream (`gen_ai.response.time_to_first_chunk`). */
|
|
123
|
+
markFirstChunk() {
|
|
124
|
+
if (this.firstChunkPerf === undefined)
|
|
125
|
+
this.firstChunkPerf = performance.now();
|
|
126
|
+
}
|
|
127
|
+
setFinishReason(index, reason) {
|
|
128
|
+
if (typeof reason !== 'string' || !reason || reason.length > 64)
|
|
129
|
+
return;
|
|
130
|
+
if (!Number.isInteger(index) || index < 0 || index > 127)
|
|
131
|
+
return;
|
|
132
|
+
this.finishReasons[index] = reason;
|
|
133
|
+
}
|
|
134
|
+
setResponseId(value) {
|
|
135
|
+
if (this.responseId === undefined && typeof value === 'string' && value.length > 0 && value.length <= 256)
|
|
136
|
+
this.responseId = value;
|
|
137
|
+
}
|
|
138
|
+
setResponseModel(value) {
|
|
139
|
+
const model = modelName(value);
|
|
140
|
+
if (model)
|
|
141
|
+
this.responseModel = model;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Fecha a observação depois de `observationIdleMs` sem leitura do corpo (resposta abandonada): o
|
|
145
|
+
* span sai com o que foi visto. Um stream longo que continua sendo lido não é cortado.
|
|
146
|
+
*/
|
|
147
|
+
armWatchdog() {
|
|
148
|
+
if (this.finished || this.watchdog)
|
|
149
|
+
return;
|
|
150
|
+
this.lastActivity = performance.now();
|
|
151
|
+
this.schedule(this.config.observationIdleMs);
|
|
152
|
+
}
|
|
153
|
+
/** Houve leitura do corpo: adia o prazo sem custo de timer por pedaço. */
|
|
154
|
+
markActivity() {
|
|
155
|
+
this.lastActivity = performance.now();
|
|
156
|
+
}
|
|
157
|
+
schedule(ms) {
|
|
158
|
+
this.watchdog = setTimeout(() => {
|
|
159
|
+
const idle = performance.now() - this.lastActivity;
|
|
160
|
+
if (idle >= this.config.observationIdleMs)
|
|
161
|
+
this.finish('unobserved');
|
|
162
|
+
else
|
|
163
|
+
this.schedule(this.config.observationIdleMs - idle);
|
|
164
|
+
}, Math.max(1, Math.ceil(ms)));
|
|
165
|
+
if (typeof this.watchdog === 'object' && this.watchdog && 'unref' in this.watchdog)
|
|
166
|
+
this.watchdog.unref();
|
|
167
|
+
}
|
|
168
|
+
onFinish(cleanup) {
|
|
169
|
+
if (this.finished)
|
|
170
|
+
cleanup();
|
|
171
|
+
else
|
|
172
|
+
this.cleanups.push(cleanup);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Encerra a chamada. `error` só vira `span.error` quando informado (falha de rede, erro do provedor
|
|
176
|
+
* no corpo); HTTP 4xx fica em `error.type` sem virar issue, como no resto do `http.client`.
|
|
177
|
+
*/
|
|
178
|
+
finish(outcome, error) {
|
|
179
|
+
if (this.finished)
|
|
180
|
+
return;
|
|
181
|
+
this.finished = true;
|
|
182
|
+
if (this.failed)
|
|
183
|
+
outcome = 'error';
|
|
184
|
+
if (this.watchdog)
|
|
185
|
+
clearTimeout(this.watchdog);
|
|
186
|
+
for (const cleanup of this.cleanups.splice(0)) {
|
|
187
|
+
try {
|
|
188
|
+
cleanup();
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
/* limpeza nunca quebra a aplicação */
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
this.applyAttributes(outcome);
|
|
196
|
+
if (error !== undefined && !this.span.error)
|
|
197
|
+
this.span.setError(error);
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
this.span.end();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
applyAttributes(outcome) {
|
|
204
|
+
const span = this.span;
|
|
205
|
+
span.setAttribute(FG_GEN_AI.outcome, outcome);
|
|
206
|
+
if (this.responseId)
|
|
207
|
+
span.setAttribute(GEN_AI.responseId, this.responseId);
|
|
208
|
+
if (this.responseModel)
|
|
209
|
+
span.setAttribute(GEN_AI.responseModel, this.responseModel);
|
|
210
|
+
const reasons = this.finishReasons.filter((reason) => typeof reason === 'string');
|
|
211
|
+
if (reasons.length > 0)
|
|
212
|
+
span.setAttribute(GEN_AI.responseFinishReasons, reasons);
|
|
213
|
+
if (this.errorType)
|
|
214
|
+
span.setAttribute(GEN_AI.errorType, this.errorType);
|
|
215
|
+
if (this.firstChunkPerf !== undefined) {
|
|
216
|
+
span.setAttribute(GEN_AI.timeToFirstChunk, Math.max(0, Math.round(this.firstChunkPerf - this.startedPerf)) / 1000);
|
|
217
|
+
}
|
|
218
|
+
const usage = this.usage;
|
|
219
|
+
const has = (value) => value !== undefined;
|
|
220
|
+
if (has(usage.input))
|
|
221
|
+
span.setAttribute(GEN_AI.inputTokens, usage.input);
|
|
222
|
+
if (has(usage.output))
|
|
223
|
+
span.setAttribute(GEN_AI.outputTokens, usage.output);
|
|
224
|
+
if (has(usage.cacheRead))
|
|
225
|
+
span.setAttribute(GEN_AI.cacheReadInputTokens, usage.cacheRead);
|
|
226
|
+
if (has(usage.cacheCreation))
|
|
227
|
+
span.setAttribute(GEN_AI.cacheCreationInputTokens, usage.cacheCreation);
|
|
228
|
+
if (has(usage.reasoning))
|
|
229
|
+
span.setAttribute(GEN_AI.reasoningOutputTokens, usage.reasoning);
|
|
230
|
+
const anyUsage = has(usage.input) || has(usage.output);
|
|
231
|
+
if (anyUsage && this.usagePartial)
|
|
232
|
+
span.setAttribute(FG_GEN_AI.usagePartial, true);
|
|
233
|
+
if (this.reportedCost) {
|
|
234
|
+
span.setAttribute(FG_GEN_AI.costAmount, this.reportedCost.amount);
|
|
235
|
+
span.setAttribute(FG_GEN_AI.costCurrency, this.reportedCost.currency);
|
|
236
|
+
span.setAttribute(FG_GEN_AI.costEstimated, false);
|
|
237
|
+
span.setAttribute(FG_GEN_AI.costSource, this.reportedCost.source);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const pricing = this.config.pricing;
|
|
241
|
+
if (!pricing || !anyUsage || this.usagePartial)
|
|
242
|
+
return;
|
|
243
|
+
const amount = estimateCost(pricing, this.provider, this.operation, [this.requestModel, this.responseModel], usage);
|
|
244
|
+
if (amount === undefined)
|
|
245
|
+
return;
|
|
246
|
+
span.setAttribute(FG_GEN_AI.costAmount, amount);
|
|
247
|
+
span.setAttribute(FG_GEN_AI.costCurrency, pricing.currency);
|
|
248
|
+
span.setAttribute(FG_GEN_AI.costEstimated, true);
|
|
249
|
+
span.setAttribute(FG_GEN_AI.costSource, pricing.source);
|
|
250
|
+
span.setAttribute(FG_GEN_AI.costPricingVersion, pricing.version);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/** Custo informado pelo provedor no corpo (`usage.cost` do OpenRouter, em créditos = USD). */
|
|
254
|
+
export function reportedCost(provider, usage) {
|
|
255
|
+
if (provider !== 'openrouter')
|
|
256
|
+
return undefined;
|
|
257
|
+
const amount = finiteNumber(usage.cost);
|
|
258
|
+
return amount !== undefined && amount >= 0 ? { amount, currency: 'USD', source: 'openrouter:usage.cost' } : undefined;
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=call.js.map
|