fg-tracker-sdk 0.10.0 → 0.13.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.
Files changed (74) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +129 -35
  3. package/dist/context.d.ts +9 -0
  4. package/dist/context.d.ts.map +1 -1
  5. package/dist/context.js +27 -0
  6. package/dist/context.js.map +1 -1
  7. package/dist/database.d.ts.map +1 -1
  8. package/dist/database.js +34 -5
  9. package/dist/database.js.map +1 -1
  10. package/dist/express.js +2 -2
  11. package/dist/express.js.map +1 -1
  12. package/dist/fetch.d.ts.map +1 -1
  13. package/dist/fetch.js +47 -10
  14. package/dist/fetch.js.map +1 -1
  15. package/dist/genai/bedrock.d.ts +22 -0
  16. package/dist/genai/bedrock.d.ts.map +1 -0
  17. package/dist/genai/bedrock.js +161 -0
  18. package/dist/genai/bedrock.js.map +1 -0
  19. package/dist/genai/call.d.ts +96 -0
  20. package/dist/genai/call.d.ts.map +1 -0
  21. package/dist/genai/call.js +260 -0
  22. package/dist/genai/call.js.map +1 -0
  23. package/dist/genai/config.d.ts +71 -0
  24. package/dist/genai/config.d.ts.map +1 -0
  25. package/dist/genai/config.js +110 -0
  26. package/dist/genai/config.js.map +1 -0
  27. package/dist/genai/http.d.ts +49 -0
  28. package/dist/genai/http.d.ts.map +1 -0
  29. package/dist/genai/http.js +795 -0
  30. package/dist/genai/http.js.map +1 -0
  31. package/dist/genai/semconv.d.ts +64 -0
  32. package/dist/genai/semconv.d.ts.map +1 -0
  33. package/dist/genai/semconv.js +52 -0
  34. package/dist/genai/semconv.js.map +1 -0
  35. package/dist/genai/sse.d.ts +26 -0
  36. package/dist/genai/sse.d.ts.map +1 -0
  37. package/dist/genai/sse.js +100 -0
  38. package/dist/genai/sse.js.map +1 -0
  39. package/dist/index.d.ts +7 -2
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +11 -3
  42. package/dist/index.js.map +1 -1
  43. package/dist/infra/aws.d.ts +45 -3
  44. package/dist/infra/aws.d.ts.map +1 -1
  45. package/dist/infra/aws.js +455 -29
  46. package/dist/infra/aws.js.map +1 -1
  47. package/dist/infra/lambda.d.ts +19 -0
  48. package/dist/infra/lambda.d.ts.map +1 -1
  49. package/dist/infra/lambda.js +61 -9
  50. package/dist/infra/lambda.js.map +1 -1
  51. package/dist/infra/messaging.d.ts +133 -0
  52. package/dist/infra/messaging.d.ts.map +1 -0
  53. package/dist/infra/messaging.js +366 -0
  54. package/dist/infra/messaging.js.map +1 -0
  55. package/dist/next.js +2 -2
  56. package/dist/next.js.map +1 -1
  57. package/dist/node-http.d.ts.map +1 -1
  58. package/dist/node-http.js +7 -3
  59. package/dist/node-http.js.map +1 -1
  60. package/dist/normalize.d.ts +32 -0
  61. package/dist/normalize.d.ts.map +1 -1
  62. package/dist/normalize.js +165 -10
  63. package/dist/normalize.js.map +1 -1
  64. package/dist/pg.d.ts +8 -3
  65. package/dist/pg.d.ts.map +1 -1
  66. package/dist/pg.js +512 -41
  67. package/dist/pg.js.map +1 -1
  68. package/dist/tracker.d.ts +17 -1
  69. package/dist/tracker.d.ts.map +1 -1
  70. package/dist/tracker.js +50 -5
  71. package/dist/tracker.js.map +1 -1
  72. package/dist/types.d.ts +26 -4
  73. package/dist/types.d.ts.map +1 -1
  74. package/package.json +7 -1
@@ -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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call.js","sourceRoot":"","sources":["../../src/genai/call.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ import type { GenAiOperation } from './semconv.js';
2
+ /** Formato de API reconhecido: Chat Completions/Embeddings (`openai`) ou Messages (`anthropic`). */
3
+ export type GenAiApiFormat = 'openai' | 'anthropic';
4
+ /** Endpoint de IA além dos embutidos (gateway OpenAI-compatível, servidor próprio). */
5
+ export interface GenAiEndpoint {
6
+ /**
7
+ * Prefixo de `origem + caminho` (`https://llm.interno:8443/v1`) ou RegExp testada contra
8
+ * `origem + caminho` (sem query). Só requisições `POST` nos caminhos do formato contam.
9
+ */
10
+ baseUrl: string | RegExp;
11
+ /** Valor de `gen_ai.provider.name` (ex.: `azure.ai.openai`, `vllm`). */
12
+ provider: string;
13
+ /** `openai`: `…/chat/completions` e `…/embeddings`. `anthropic`: `…/messages`. */
14
+ api: GenAiApiFormat;
15
+ }
16
+ /** Preço de um modelo, por 1 milhão de tokens, na moeda da tabela. */
17
+ export interface GenAiModelPrice {
18
+ /** Restringe a entrada a um `gen_ai.provider.name`; sem ele, vale para qualquer provedor. */
19
+ provider?: string;
20
+ /** Nome exato do modelo, comparado com o pedido e, sem entrada para ele, com o devolvido. */
21
+ model: string;
22
+ inputPerMillion: number;
23
+ /** Obrigatório para estimar `chat`; dispensável em `embeddings`. */
24
+ outputPerMillion?: number;
25
+ /** Sem ele, chamada com tokens lidos do cache fica sem estimativa. */
26
+ cacheReadPerMillion?: number;
27
+ /** Sem ele, chamada com tokens gravados no cache fica sem estimativa. */
28
+ cacheCreationPerMillion?: number;
29
+ }
30
+ /**
31
+ * Tabela de preços para o custo estimado. O SDK não traz preços embutidos: sem tabela, só aparece
32
+ * o custo que o provedor informa (OpenRouter). Toda estimativa leva fonte, versão e moeda.
33
+ */
34
+ export interface GenAiPricing {
35
+ /** De onde vieram os preços (URL da página de preços, contrato). Vai em `fg.gen_ai.cost.source`. */
36
+ source: string;
37
+ /** Versão ou data da tabela (`2026-10-01`). Vai em `fg.gen_ai.cost.pricing_version`. */
38
+ version: string;
39
+ /** Moeda ISO 4217 (`USD`, `BRL`). */
40
+ currency: string;
41
+ models: GenAiModelPrice[];
42
+ }
43
+ export interface GenAiOptions {
44
+ /** Endpoints além de `api.openai.com`, `openrouter.ai/api` e `api.anthropic.com`; vencem os embutidos. */
45
+ endpoints?: GenAiEndpoint[];
46
+ /** Tabela para o custo estimado (`fg.gen_ai.cost.estimated = true`). Inválida = ignorada com aviso. */
47
+ pricing?: GenAiPricing;
48
+ /**
49
+ * Prazo sem leitura do corpo de uma resposta (stream abandonado, resposta que ninguém lê) para
50
+ * encerrar a observação: o span fecha com `fg.gen_ai.outcome = 'unobserved'`. Conta a partir da
51
+ * última leitura; um stream longo que continua sendo lido não é cortado. Default 15 min.
52
+ */
53
+ observationIdleMs?: number;
54
+ }
55
+ export interface ResolvedGenAi {
56
+ endpoints: GenAiEndpoint[];
57
+ pricing: GenAiPricing | null;
58
+ observationIdleMs: number;
59
+ }
60
+ /** Endpoint reconhecido para uma requisição. */
61
+ export interface GenAiTarget {
62
+ provider: string;
63
+ api: GenAiApiFormat;
64
+ operation: GenAiOperation;
65
+ }
66
+ /** Tabela válida ou o motivo da recusa. Uma entrada ruim invalida a tabela inteira: nada de estimativa com dado duvidoso. */
67
+ export declare function validatePricing(pricing: GenAiPricing): string | null;
68
+ export declare function resolveGenAi(options: GenAiOptions | false | undefined, warn: (message: string) => void): ResolvedGenAi | null;
69
+ /** Endpoint de IA de uma requisição, ou `null` (método diferente de POST, host ou caminho desconhecido). */
70
+ export declare function matchGenAiTarget(config: ResolvedGenAi, method: string, url: URL): GenAiTarget | null;
71
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/genai/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,oGAAoG;AACpG,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEpD,uFAAuF;AACvF,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,GAAG,EAAE,cAAc,CAAC;CACrB;AAED,sEAAsE;AACtE,MAAM,WAAW,eAAe;IAC9B,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6FAA6F;IAC7F,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sEAAsE;IACtE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,yEAAyE;IACzE,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,oGAAoG;IACpG,MAAM,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,0GAA0G;IAC1G,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;IAC5B,uGAAuG;IACvG,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7B,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,gDAAgD;AAChD,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,cAAc,CAAC;IACpB,SAAS,EAAE,cAAc,CAAC;CAC3B;AAmBD,6HAA6H;AAC7H,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAepE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,GAAG,SAAS,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,aAAa,GAAG,IAAI,CAuB7H;AA4BD,4GAA4G;AAC5G,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,WAAW,GAAG,IAAI,CAUpG"}
@@ -0,0 +1,110 @@
1
+ const DEFAULT_OBSERVATION_IDLE_MS = 15 * 60_000;
2
+ /** Ordem importa: os do usuário vêm antes e vencem. */
3
+ const BUILT_IN_ENDPOINTS = [
4
+ { baseUrl: 'https://api.openai.com/', provider: 'openai', api: 'openai' },
5
+ { baseUrl: 'https://openrouter.ai/api/', provider: 'openrouter', api: 'openai' },
6
+ { baseUrl: 'https://api.anthropic.com/', provider: 'anthropic', api: 'anthropic' },
7
+ ];
8
+ const NAME_RE = /^[A-Za-z0-9][A-Za-z0-9_.:/-]{0,127}$/;
9
+ const CURRENCY_RE = /^[A-Z]{3}$/;
10
+ function validPrice(value, required) {
11
+ if (value === undefined)
12
+ return !required;
13
+ return typeof value === 'number' && Number.isFinite(value) && value >= 0;
14
+ }
15
+ /** Tabela válida ou o motivo da recusa. Uma entrada ruim invalida a tabela inteira: nada de estimativa com dado duvidoso. */
16
+ export function validatePricing(pricing) {
17
+ if (!pricing || typeof pricing !== 'object')
18
+ return 'tabela ausente';
19
+ if (typeof pricing.source !== 'string' || !pricing.source.trim() || pricing.source.length > 300)
20
+ return '`source` obrigatório (até 300 caracteres)';
21
+ if (typeof pricing.version !== 'string' || !pricing.version.trim() || pricing.version.length > 100)
22
+ return '`version` obrigatório (até 100 caracteres)';
23
+ if (typeof pricing.currency !== 'string' || !CURRENCY_RE.test(pricing.currency))
24
+ return '`currency` precisa ser um código ISO 4217 em maiúsculas (ex.: USD)';
25
+ if (!Array.isArray(pricing.models) || pricing.models.length === 0)
26
+ return '`models` vazio';
27
+ for (const [index, entry] of pricing.models.entries()) {
28
+ if (!entry || typeof entry.model !== 'string' || !entry.model.trim())
29
+ return `models[${index}]: \`model\` obrigatório`;
30
+ if (entry.provider !== undefined && (typeof entry.provider !== 'string' || !entry.provider.trim()))
31
+ return `models[${index}]: \`provider\` inválido`;
32
+ if (!validPrice(entry.inputPerMillion, true))
33
+ return `models[${index}]: \`inputPerMillion\` precisa ser um número ≥ 0`;
34
+ for (const key of ['outputPerMillion', 'cacheReadPerMillion', 'cacheCreationPerMillion']) {
35
+ if (!validPrice(entry[key], false))
36
+ return `models[${index}]: \`${key}\` precisa ser um número ≥ 0`;
37
+ }
38
+ }
39
+ return null;
40
+ }
41
+ export function resolveGenAi(options, warn) {
42
+ if (options === false)
43
+ return null;
44
+ const endpoints = [];
45
+ for (const endpoint of options?.endpoints ?? []) {
46
+ const validBase = endpoint && (endpoint.baseUrl instanceof RegExp || (typeof endpoint.baseUrl === 'string' && /^https?:\/\/./.test(endpoint.baseUrl)));
47
+ if (!validBase || typeof endpoint.provider !== 'string' || !NAME_RE.test(endpoint.provider) || (endpoint.api !== 'openai' && endpoint.api !== 'anthropic')) {
48
+ warn('genAi.endpoints: entrada ignorada (precisa de baseUrl http(s) ou RegExp, provider e api `openai`|`anthropic`)');
49
+ continue;
50
+ }
51
+ endpoints.push(endpoint);
52
+ }
53
+ let pricing = null;
54
+ if (options?.pricing !== undefined) {
55
+ const problem = validatePricing(options.pricing);
56
+ if (problem)
57
+ warn(`genAi.pricing ignorada, custo não será estimado: ${problem}`);
58
+ else
59
+ pricing = options.pricing;
60
+ }
61
+ const idle = options?.observationIdleMs;
62
+ return {
63
+ endpoints: [...endpoints, ...BUILT_IN_ENDPOINTS],
64
+ pricing,
65
+ observationIdleMs: typeof idle === 'number' && Number.isFinite(idle) && idle > 0 ? idle : DEFAULT_OBSERVATION_IDLE_MS,
66
+ };
67
+ }
68
+ function operationFor(api, pathname) {
69
+ const path = pathname.replace(/\/+$/, '');
70
+ if (api === 'openai') {
71
+ if (path.endsWith('/chat/completions'))
72
+ return 'chat';
73
+ if (path.endsWith('/embeddings'))
74
+ return 'embeddings';
75
+ return null;
76
+ }
77
+ // `/v1/messages/count_tokens` e `/v1/messages/batches` não são geração.
78
+ return path.endsWith('/messages') ? 'chat' : null;
79
+ }
80
+ function matchesBase(base, target) {
81
+ if (base instanceof RegExp) {
82
+ if (!base.global && !base.sticky)
83
+ return base.test(target);
84
+ const previous = base.lastIndex;
85
+ base.lastIndex = 0;
86
+ try {
87
+ return base.test(target);
88
+ }
89
+ finally {
90
+ base.lastIndex = previous;
91
+ }
92
+ }
93
+ const prefix = base.replace(/\/+$/, '');
94
+ return target === prefix || target.startsWith(`${prefix}/`);
95
+ }
96
+ /** Endpoint de IA de uma requisição, ou `null` (método diferente de POST, host ou caminho desconhecido). */
97
+ export function matchGenAiTarget(config, method, url) {
98
+ if (method !== 'POST')
99
+ return null;
100
+ const target = `${url.origin}${url.pathname}`;
101
+ for (const endpoint of config.endpoints) {
102
+ if (!matchesBase(endpoint.baseUrl, target))
103
+ continue;
104
+ const operation = operationFor(endpoint.api, url.pathname);
105
+ // O primeiro endpoint que casa decide: um gateway do usuário não cai num embutido.
106
+ return operation ? { provider: endpoint.provider, api: endpoint.api, operation } : null;
107
+ }
108
+ return null;
109
+ }
110
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/genai/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import type { ActiveSpan } from '../types.js';
2
+ import { GenAiCall } from './call.js';
3
+ import type { GenAiApiFormat, GenAiTarget, ResolvedGenAi } from './config.js';
4
+ export declare function isAbortError(err: unknown): boolean;
5
+ /**
6
+ * Prende o estado GenAI ao span `http.client` de uma requisição reconhecida. Lê só o corpo em
7
+ * texto/bytes já em memória (o SDK da OpenAI e o da Anthropic mandam string); `Request` com corpo
8
+ * em stream não é lido, e o modelo sai da resposta.
9
+ */
10
+ export declare function startHttpGenAiCall(config: ResolvedGenAi, span: ActiveSpan, target: GenAiTarget, input: string | URL | Request, init: RequestInit | undefined, startedPerf?: number): GenAiCall;
11
+ /**
12
+ * A requisição falhou antes da resposta (rede, abort). Cancelamento não é erro: vale o `AbortError` e
13
+ * também o `signal` abortado com motivo próprio (`controller.abort(new Error(...))`), com que o
14
+ * `fetch` rejeita no lugar do `AbortError`.
15
+ */
16
+ export declare function failHttpGenAiCall(call: GenAiCall, err: unknown, signal?: AbortSignal | null): void;
17
+ /**
18
+ * Pares chave → valor da **raiz** de um objeto JSON lidos de trás para frente a partir do fim do
19
+ * texto (o fim do corpo), sem precisar do começo. Lendo de trás para frente a partir do `}` final,
20
+ * sempre se sabe se a posição está dentro de uma string (cada `"` não escapado alterna) e a que
21
+ * profundidade: só pares na profundidade 1 entram. A leitura para no primeiro ponto que o corte do
22
+ * trecho deixa ambíguo; o que começa antes dele é ignorado.
23
+ */
24
+ export declare function rootEntriesFromEnd(text: string): Record<string, unknown>;
25
+ /**
26
+ * Estrutura de um corpo JSON acompanhada byte a byte desde o início, sem guardar o corpo: profundidade
27
+ * de `{`/`[` fora de strings (cada `"` não escapado alterna; `\` escapa o byte seguinte). Os bytes que
28
+ * importam (`"`, `\`, chaves e colchetes) são ASCII e nunca aparecem dentro de um caractere UTF-8 de
29
+ * vários bytes, então os pedaços podem ser lidos como chegam, partidos em qualquer ponto.
30
+ * `complete` = o corpo é um objeto (`{` como primeiro byte não branco) cuja raiz fecha uma vez, no
31
+ * fim, com no máximo espaço em branco depois.
32
+ */
33
+ export declare class JsonShape {
34
+ private depth;
35
+ private inString;
36
+ private escaped;
37
+ private started;
38
+ private closed;
39
+ private broken;
40
+ push(bytes: Uint8Array): void;
41
+ get complete(): boolean;
42
+ }
43
+ /**
44
+ * Observa a resposta e devolve a que vai para quem chamou: uma equivalente cujo corpo é repassado
45
+ * sob demanda e observado no caminho (JSON e stream SSE, sem leitura adiantada; erro HTTP numa cópia
46
+ * limitada), ou a própria quando não dá para observar. O span fecha quando a observação termina.
47
+ */
48
+ export declare function observeHttpGenAiResponse(call: GenAiCall, res: Response, api: GenAiApiFormat, signal: AbortSignal | null | undefined): Response;
49
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/genai/http.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAe,SAAS,EAAwD,MAAM,WAAW,CAAC;AACzG,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAyC9E,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAGlD;AAuDD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAC7B,IAAI,EAAE,WAAW,GAAG,SAAS,EAC7B,WAAW,GAAE,MAA0B,GACtC,SAAS,CASX;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CASlG;AA2HD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAwCxE;AAqTD;;;;;;;GAOG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;IAEvB,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IA0B7B,IAAI,QAAQ,IAAI,OAAO,CAEtB;CACF;AAiGD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,QAAQ,CAqC9I"}