fg-tracker-sdk 0.11.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 (43) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +76 -32
  3. package/dist/fetch.d.ts.map +1 -1
  4. package/dist/fetch.js +31 -2
  5. package/dist/fetch.js.map +1 -1
  6. package/dist/genai/bedrock.d.ts +22 -0
  7. package/dist/genai/bedrock.d.ts.map +1 -0
  8. package/dist/genai/bedrock.js +161 -0
  9. package/dist/genai/bedrock.js.map +1 -0
  10. package/dist/genai/call.d.ts +96 -0
  11. package/dist/genai/call.d.ts.map +1 -0
  12. package/dist/genai/call.js +260 -0
  13. package/dist/genai/call.js.map +1 -0
  14. package/dist/genai/config.d.ts +71 -0
  15. package/dist/genai/config.d.ts.map +1 -0
  16. package/dist/genai/config.js +110 -0
  17. package/dist/genai/config.js.map +1 -0
  18. package/dist/genai/http.d.ts +49 -0
  19. package/dist/genai/http.d.ts.map +1 -0
  20. package/dist/genai/http.js +795 -0
  21. package/dist/genai/http.js.map +1 -0
  22. package/dist/genai/semconv.d.ts +64 -0
  23. package/dist/genai/semconv.d.ts.map +1 -0
  24. package/dist/genai/semconv.js +52 -0
  25. package/dist/genai/semconv.js.map +1 -0
  26. package/dist/genai/sse.d.ts +26 -0
  27. package/dist/genai/sse.d.ts.map +1 -0
  28. package/dist/genai/sse.js +100 -0
  29. package/dist/genai/sse.js.map +1 -0
  30. package/dist/index.d.ts +3 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +1 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/infra/aws.d.ts.map +1 -1
  35. package/dist/infra/aws.js +38 -2
  36. package/dist/infra/aws.js.map +1 -1
  37. package/dist/tracker.d.ts +9 -0
  38. package/dist/tracker.d.ts.map +1 -1
  39. package/dist/tracker.js +14 -0
  40. package/dist/tracker.js.map +1 -1
  41. package/dist/types.d.ts +9 -0
  42. package/dist/types.d.ts.map +1 -1
  43. package/package.json +4 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog — fg-tracker-sdk
2
2
 
3
+ ## 0.13.0
4
+
5
+ Chamadas de IA generativa (CONTRATO.md §30, docs/genai.md). Nada removido; ligado por padrão só onde já havia instrumentação (`fetch` instrumentado e `instrumentAws`), e `genAi: false` desliga.
6
+
7
+ - **Atributos `gen_ai.*` da semconv OpenTelemetry 1.41.1** (`GEN_AI_SEMCONV_VERSION`, e `fg.gen_ai.semconv_version` em cada span) no próprio span da chamada, sem span novo: o `http.client` do `fetch` instrumentado para OpenAI Chat Completions/Embeddings (`api.openai.com`), OpenRouter (`openrouter.ai/api`) e Anthropic Messages (`api.anthropic.com`), e o span de `instrumentAws` para Bedrock Runtime `Converse`/`ConverseStream`. Gateways compatíveis entram por `genAi.endpoints`.
8
+ - Modelo pedido e devolvido, provedor, parâmetros numéricos, motivo de parada, tempo até o primeiro pedaço do stream, `error.type`, `http.request.resend_count` (retentativas dos clientes OpenAI/Anthropic) e o desfecho em `fg.gen_ai.outcome` (`completed`, `incomplete`, `cancelled`, `error`, `unobserved`).
9
+ - **Uso de tokens só quando o provedor informa** (`gen_ai.usage.*`, entrada já com o cache). Nada é estimado nem pedido: o SDK não acrescenta `stream_options` ao pedido. Stream da Anthropic cancelado antes do total leva só a entrada, com `fg.gen_ai.usage.partial`.
10
+ - **Custo**: o informado pelo OpenRouter (`usage.cost`, USD) ou, com `genAi.pricing` (fonte, versão, moeda e preços por milhão de tokens), uma estimativa marcada `fg.gen_ai.cost.estimated = true`. O SDK não traz preços; tabela inválida é ignorada com aviso.
11
+ - **Privacidade**: prompts, mensagens, instruções de sistema, ferramentas, respostas, identificadores de usuário e mensagens de erro do provedor nunca viram atributo. Exceção do Bedrock passa a ir para o `span.error` com o nome da exceção e sem a mensagem do serviço.
12
+ - **Efeito ao atualizar:** com o `fetch` instrumentado, a resposta desses provedores (JSON ou stream) chega à aplicação como uma `Response` equivalente (mesmo status, headers e `url`) cujo corpo passa intacto pela observação. O corpo é um byte stream como o do `fetch` e aceita leitor BYOB. O span `http.client` dessas chamadas passa a durar até o fim do corpo (antes, até os headers), e um 4xx/5xx ganha `error.type`. Num 2xx o corpo é observado enquanto a aplicação o lê, sem leitura adiantada, e a contrapressão da conexão não muda; corpo que ninguém lê fecha o span como `unobserved` depois de `observationIdleMs`. Cancelamento do corpo (`break`, `abort`, `body.cancel()`) fecha a conexão como antes. Numa resposta 2xx o span fecha como `cancelled`, sem `error`; num 4xx/5xx o desfecho segue `error`, com até 50 ms e 64 KiB para ler o código do provedor. `genAi: false` volta ao comportamento anterior.
13
+ - Novos exports: `GEN_AI_SEMCONV_VERSION` e os tipos `GenAiOptions`, `GenAiEndpoint`, `GenAiPricing`, `GenAiModelPrice`, `GenAiApiFormat`, `GenAiOperation`, `GenAiOutcome`.
14
+
3
15
  ## 0.11.0
4
16
 
5
17
  Contexto distribuído por filas, eventos, banco e transações (CONTRATO.md §16.1). Nada removido; a forma antiga de cada API continua valendo.
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Instrumentação Node para o [FG Tracker](../../README.md) e envio de logs (pino, winston, NestJS Logger). Única dependência de runtime: `pino-abstract-transport` (a instrumentação AWS usa tipos estruturais, sem `@aws-sdk/*`); usa `AsyncLocalStorage` para encadear spans e o `fetch` global do Node para enviar lotes ao `POST /v1/ingest`.
4
4
 
5
+ Guia público (conta, chaves, primeiro trace, integrações e diagnóstico): <https://tracker-site.fortground.com.br/docs/node/>.
6
+
5
7
  Requisitos: Node ≥ 20 (recomendado 24). O pacote é ESM; em projetos CommonJS (NestJS) funciona com `require()` no Node ≥ 22.12 ou via `import()` dinâmico.
6
8
 
7
9
  ## Instalação
@@ -14,7 +16,7 @@ pnpm add fg-tracker-sdk # publicado no npm
14
16
 
15
17
  ## Endpoint
16
18
 
17
- O default de `endpoint` é `http://localhost:4050` (constante `DEFAULT_ENDPOINT`, API local do monorepo). Em produção passe a URL da API mostrada no painel em **Conectar** (por exemplo via `FG_TRACKER_URL`). O endereço de produção da API é `https://tracker-api.fortground.com.br` (o antigo `track-api.fortground.com.br` continua respondendo).
19
+ O default de `endpoint` é `http://localhost:4050` (constante `DEFAULT_ENDPOINT`, API local do monorepo). Em produção passe a URL da API mostrada no painel em **Conectar**: o `createTracker` **não lê** `FG_TRACKER_URL` nem `FG_TRACKER_KEY` sozinho (só os CLIs leem), então passe `endpoint: process.env.FG_TRACKER_URL` e `apiKey: process.env.FG_TRACKER_KEY`. O endereço de produção da API é `https://tracker-api.fortground.com.br` (o antigo `track-api.fortground.com.br` continua respondendo).
18
20
 
19
21
  ## API
20
22
 
@@ -22,8 +24,8 @@ O default de `endpoint` é `http://localhost:4050` (constante `DEFAULT_ENDPOINT`
22
24
  import { createTracker } from 'fg-tracker-sdk';
23
25
 
24
26
  const tracker = createTracker({
25
- apiKey: process.env.FG_TRACKER_KEY!, // stk_...
26
- endpoint: 'http://localhost:4050', // default
27
+ apiKey: process.env.FG_TRACKER_KEY!, // chave de servidor fgsk_... (nunca no navegador)
28
+ endpoint: process.env.FG_TRACKER_URL, // sem ele: http://localhost:4050
27
29
  serviceName: 'minha-app',
28
30
  flushIntervalMs: 2000, // default
29
31
  });
@@ -38,7 +40,7 @@ await tracker.flush(); await tracker.shutdown(); // shutdown espera no máximo
38
40
 
39
41
  | opção | default | descrição |
40
42
  |---|---|---|
41
- | `apiKey` | — | chave do projeto; sem ela o tracker vira no-op (com aviso) |
43
+ | `apiKey` | — | chave de servidor do projeto (`fgsk_…`, Configurações → Chaves de ingestão); sem ela o tracker vira no-op (com aviso) |
42
44
  | `endpoint` | `http://localhost:4050` | URL base da API |
43
45
  | `serviceName` | — | vai em `attributes.serviceName` de todo span |
44
46
  | `flushIntervalMs` | `2000` | intervalo de envio |
@@ -65,8 +67,8 @@ await tracker.flush(); await tracker.shutdown(); // shutdown espera no máximo
65
67
  | `tracesSampleRate` | `1` | fração de traces enviados (decidida na raiz; filhos seguem; `traceparent` sai com `-00` quando não amostrado) |
66
68
  | `tracesSampler` | — | `(ctx: { kind, name, attributes, parentSampled? }) => taxa` por operação (ex.: `0` para `GET /health`) |
67
69
  | `ignoreTransactions` | `[]` | nomes de raiz (string/RegExp) que nunca são enviados |
68
- | `ignoreErrors` | `[]` | erros (`name: message`, string/RegExp) que não viram issue (`attributes.ignoredError = true`) |
69
- | `scrubFields` | `DEFAULT_SCRUB_FIELDS` | chaves de atributos que viram `[Filtered]` (password, token, authorization, cookie, cpf…); `false` desliga |
70
+ | `ignoreErrors` | `[]` | erros (`name: message`, string/RegExp) que não viram issue (o span vai sem `error`, com `attributes.ignoredError = <nome do erro>`) |
71
+ | `scrubFields` | `DEFAULT_SCRUB_FIELDS` | chaves de atributos que viram `[Filtered]` (password, secret, token, authorization, cookie, api_key, access_token, card_number…; CPF e e-mail não estão na lista padrão); `false` desliga |
70
72
  | `beforeSend` | — | `(span) => span \| null`, último filtro antes da fila |
71
73
  | `infra` | `{ enabled: true, idleIntervalMs: 60000 }` | agregados de infraestrutura (veja **Infraestrutura**); nunca amostrados |
72
74
 
@@ -98,8 +100,8 @@ tracker.currentTraceId(); // para logs / x-trace-
98
100
  await tracker.checkIn('backup-diario', () => runBackup(), { intervalSeconds: 86400, graceSeconds: 600 }); // monitor de job
99
101
  const run = await tracker.monitor('sync').start(); /* ... */ await run.ok(); // ou run.fail(err)
100
102
 
101
- await tracker.registerRelease({ commitSha: process.env.GITHUB_SHA, repository: 'FORTGROUND/phase' }); // ou o CLI:
102
- // FG_TRACKER_KEY=stk_... npx fg-tracker-release --service minha-app --previous-sha $PREV --env production
103
+ await tracker.registerRelease({ commitSha: process.env.GITHUB_SHA, repository: 'sua-org/minha-app' }); // ou o CLI:
104
+ // FG_TRACKER_KEY=fgsk_... FG_TRACKER_URL=https://tracker-api.fortground.com.br npx fg-tracker-release --service minha-app --previous-sha $PREV --env production
103
105
  ```
104
106
 
105
107
  O CLI `fg-tracker-release` lê `git log`/`git show` e envia commits, arquivos e intervalos de linhas alterados: o painel usa isso para apontar **commits/PRs suspeitos** em cada issue e comparar a latência de cada rota entre releases.
@@ -108,9 +110,9 @@ Jobs com crontab: `tracker.checkIn('relatorio', run, { cron: '0 3 * * *', timezo
108
110
 
109
111
  Ambientes nos check-ins: o check-in leva o `environment` do tracker, e cada ambiente tem o seu monitor (status, histórico e alertas) para o mesmo slug, então um job de dev não mascara um de produção que parou. `{ environment: 'staging' }` troca o ambiente de um job e `{ environment: false }` envia sem ambiente (vai para o monitor sem ambiente, o de clientes anteriores). Na primeira vez que um ambiente faz check-in, o monitor dele herda nome e configuração do monitor existente com o mesmo slug. O monitor sem ambiente antigo continua sendo cobrado (um cliente antigo pode ter parado) e aparece como "legado" quando os check-ins com ambiente chegam depois do último sem ambiente: depois de atualizar todos os clientes, pause ou remova esse monitor, ou atribua a ele um ambiente na edição para manter o histórico.
110
112
 
111
- Source maps do front (para o `fg-tracker-browser`): `FG_TRACKER_KEY=stk_... npx fg-tracker-sourcemaps --dir dist --release web@$GITHUB_SHA --url-prefix ~/assets` sobe os `.map` de uma release; a API traduz o stack minificado na ingestão.
113
+ Source maps do front (para o `fg-tracker-browser`): `FG_TRACKER_KEY=fgsk_... FG_TRACKER_URL=https://tracker-api.fortground.com.br npx fg-tracker-sourcemaps --dir dist --release web@$GITHUB_SHA --url-prefix ~/assets` sobe os `.map` de uma release; a API traduz o stack minificado na ingestão.
112
114
 
113
- Falhas de envio são logadas **uma vez** e o lote é reenviado no próximo flush (respostas 4xx descartam o lote — normalmente é chave inválida). O timer usa `unref()`, então não segura o processo vivo; chame `shutdown()` no encerramento para não perder os últimos spans.
115
+ Falhas de envio são logadas **uma vez** (até um envio dar certo). Rede, 408, 429 e 5xx voltam para a fila com backoff; outras respostas 4xx descartam o lote — normalmente é chave inválida. O timer usa `unref()`, então não segura o processo vivo; chame `shutdown()` no encerramento para não perder os últimos spans.
114
116
 
115
117
  ## Express
116
118
 
@@ -118,7 +120,7 @@ Falhas de envio são logadas **uma vez** e o lote é reenviado no próximo flush
118
120
  import express from 'express';
119
121
  import { createTracker } from 'fg-tracker-sdk';
120
122
 
121
- const tracker = createTracker({ apiKey: process.env.FG_TRACKER_KEY!, serviceName: 'minha-api' });
123
+ const tracker = createTracker({ apiKey: process.env.FG_TRACKER_KEY!, endpoint: process.env.FG_TRACKER_URL, serviceName: 'minha-api' });
122
124
  const app = express();
123
125
  app.use(tracker.express()); // ANTES de qualquer outro middleware/rota
124
126
  app.get('/users/:id', async (req, res) => { /* ... */ });
@@ -136,7 +138,7 @@ import { NestFactory } from '@nestjs/core';
136
138
  import { createTracker } from 'fg-tracker-sdk';
137
139
  import { AppModule } from './app.module';
138
140
 
139
- export const tracker = createTracker({ apiKey: process.env.FG_TRACKER_KEY!, serviceName: 'minha-api' });
141
+ export const tracker = createTracker({ apiKey: process.env.FG_TRACKER_KEY!, endpoint: process.env.FG_TRACKER_URL, serviceName: 'minha-api' });
140
142
 
141
143
  async function bootstrap() {
142
144
  const app = await NestFactory.create(AppModule);
@@ -154,7 +156,7 @@ Para encerrar limpo, chame `tracker.shutdown()` num `OnApplicationShutdown` (ou
154
156
  ```ts
155
157
  // src/lib/tracker.ts
156
158
  import { createTracker } from 'fg-tracker-sdk';
157
- export const tracker = createTracker({ apiKey: process.env.FG_TRACKER_KEY!, serviceName: 'meu-site' });
159
+ export const tracker = createTracker({ apiKey: process.env.FG_TRACKER_KEY!, endpoint: process.env.FG_TRACKER_URL, serviceName: 'meu-site' });
158
160
 
159
161
  // app/api/users/[id]/route.ts
160
162
  import { tracker } from '@/lib/tracker';
@@ -164,7 +166,7 @@ export const GET = tracker.wrapNextRoute(async (req, ctx) => {
164
166
  });
165
167
  ```
166
168
 
167
- O span `http.server` recebe `${method} ${pathname normalizado}` e o status da `Response`. Só funciona no runtime Node (não no Edge). Em `output: 'export'` não há route handlers dinâmicos, então nada a instrumentar.
169
+ O span `http.server` recebe `${method} ${pathname normalizado}` e o status da `Response`. Só funciona no runtime Node (não no Edge) e só em route handlers do App Router; não espera o envio no fim da requisição, então em plataformas que congelam a função (Vercel e afins) a fila pode se perder. Em `output: 'export'` não há route handlers dinâmicos, então nada a instrumentar.
168
170
 
169
171
  ## pg
170
172
 
@@ -243,6 +245,46 @@ createTracker({ apiKey, serviceName: 'minha-app', instrumentNodeHttp: false });
243
245
 
244
246
  > Chamadas feitas direto pela API do `undici` (e não por `fetch`) não passam por `node:http` e não são cobertas; use `tracker.wrapFetch()` nesse caso.
245
247
 
248
+ ## Chamadas de IA (GenAI)
249
+
250
+ Chamadas a modelos de IA feitas pelo `fetch` instrumentado ou por um client Bedrock instrumentado ganham atributos `gen_ai.*` da [semconv OpenTelemetry 1.41.1](https://github.com/open-telemetry/semantic-conventions/tree/v1.41.1/docs/gen-ai) no **próprio** span da chamada — nenhum span novo, nenhum duplicado com a instrumentação HTTP. Ligado por padrão (`genAi: false` desliga).
251
+
252
+ ```ts
253
+ import OpenAI from 'openai';
254
+ import Anthropic from '@anthropic-ai/sdk';
255
+ import { BedrockRuntimeClient } from '@aws-sdk/client-bedrock-runtime';
256
+
257
+ const openai = new OpenAI({ fetch: tracker.wrapFetch() }); // ou instrumentGlobalFetch: true
258
+ const anthropic = new Anthropic({ fetch: tracker.wrapFetch() });
259
+ const bedrock = tracker.instrumentAws(new BedrockRuntimeClient({})); // Converse e ConverseStream
260
+ ```
261
+
262
+ | cliente | testado com | operações |
263
+ |---|---|---|
264
+ | `openai` (e OpenRouter por `fetch`, formato OpenAI) | 7.34.0 | Chat Completions (stream inclusive), Embeddings |
265
+ | `@anthropic-ai/sdk` | 0.133.0 | Messages (stream inclusive) |
266
+ | `@aws-sdk/client-bedrock-runtime` | 3.1132.0 | `Converse`, `ConverseStream` |
267
+ | gateway OpenAI/Anthropic-compatível | — | via `genAi.endpoints: [{ baseUrl, provider, api: 'openai' \| 'anthropic' }]` |
268
+
269
+ - **O que vai**: modelo pedido e devolvido, provedor, parâmetros numéricos, motivo de parada, `fg.gen_ai.outcome` (`completed`, `incomplete`, `cancelled`, `error`, `unobserved`), tempo até o primeiro pedaço no stream, `error.type` e, **só quando o provedor informa**, o uso de tokens (`gen_ai.usage.*`; a entrada inclui o cache).
270
+ - **Stream**: a aplicação recebe o corpo intacto; o span fecha no fim da geração. Cancelar (`break`, `controller.abort()`) fecha como `cancelled`, sem erro, e a conexão fecha como antes. Stream da Anthropic cancelado antes do total: só a entrada, com `fg.gen_ai.usage.partial = true`.
271
+ - **Custo**: o informado pelo OpenRouter (`usage.cost`) ou, com `genAi.pricing`, uma estimativa pela **sua** tabela, sempre com fonte, versão e moeda (`fg.gen_ai.cost.estimated = true`). O SDK não traz preços; sem uso completo ou sem preço para o modelo, não há custo.
272
+
273
+ ```ts
274
+ createTracker({
275
+ apiKey, serviceName: 'minha-api', instrumentGlobalFetch: true,
276
+ genAi: {
277
+ pricing: {
278
+ source: 'https://openai.com/api/pricing', version: '2026-10-01', currency: 'USD',
279
+ models: [{ provider: 'openai', model: 'gpt-5-mini', inputPerMillion: 0.25, cacheReadPerMillion: 0.025, outputPerMillion: 2 }],
280
+ },
281
+ },
282
+ });
283
+ ```
284
+
285
+ - **Privacidade**: prompts, mensagens, instruções de sistema, ferramentas, respostas, identificadores de usuário e mensagens de erro do provedor **nunca** viram atributo; não há opção de captura de conteúdo nesta versão.
286
+ - Como todo `http.client`, só há span dentro de um trace (requisição, `tracker.span`, `checkIn`, `trackMessage`). Matriz completa, regras e limites: [docs/genai.md](https://github.com/FORTGROUND/fg-tracker/blob/main/docs/genai.md).
287
+
246
288
  ## Spans por método, automáticos (NestJS)
247
289
 
248
290
  Instrumentar à mão só cobre o que alguém já suspeitava. O trecho que some do waterfall é justamente aquele em que ninguém pensou. Uma linha no bootstrap envolve **todos** os providers e controllers:
@@ -317,25 +359,27 @@ tracker.currentSpan()?.setAttribute('userId', user.id);
317
359
  Não precisa do SDK: basta enviar o body do contrato com a chave no header.
318
360
 
319
361
  ```bash
320
- curl -X POST http://localhost:4050/v1/ingest \
362
+ # startedAt precisa estar dentro da retenção (14 dias) e no máximo 5 min no futuro; ids novos a cada envio
363
+ TRACE_ID=$(openssl rand -hex 16); ROOT=$(openssl rand -hex 8); CHILD=$(openssl rand -hex 8); NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
364
+ curl -X POST "$FG_TRACKER_URL/v1/ingest" \
321
365
  -H 'content-type: application/json' \
322
- -H 'x-fg-tracker-key: stk_demo_0000000000000000000000000000' \
366
+ -H "x-fg-tracker-key: $FG_TRACKER_KEY" \
323
367
  -d '{
324
368
  "traces": [{
325
- "traceId": "0123456789abcdef0123456789abcdef",
369
+ "traceId": "'"$TRACE_ID"'",
326
370
  "spans": [
327
- { "spanId": "0123456789abcdef", "parentSpanId": null, "kind": "http.server", "name": "GET /users/:id",
371
+ { "spanId": "'"$ROOT"'", "parentSpanId": null, "kind": "http.server", "name": "GET /users/:id",
328
372
  "method": "GET", "route": "/users/:id", "statusCode": 200,
329
- "startedAt": "2026-09-12T12:00:00.000Z", "endedAt": "2026-09-12T12:00:00.123Z", "durationMs": 123.4,
330
- "attributes": { "url": "/users/42" }, "error": null },
331
- { "spanId": "fedcba9876543210", "parentSpanId": "0123456789abcdef", "kind": "db.query",
373
+ "startedAt": "'"$NOW"'", "endedAt": "'"$NOW"'", "durationMs": 123.4,
374
+ "attributes": { "url": "/users/42", "serviceName": "minha-api" }, "error": null },
375
+ { "spanId": "'"$CHILD"'", "parentSpanId": "'"$ROOT"'", "kind": "db.query",
332
376
  "name": "select * from users where id = ?", "method": null, "route": null, "statusCode": null,
333
- "startedAt": "2026-09-12T12:00:00.010Z", "endedAt": "2026-09-12T12:00:00.050Z", "durationMs": 40,
377
+ "startedAt": "'"$NOW"'", "endedAt": "'"$NOW"'", "durationMs": 40,
334
378
  "attributes": { "rows": 1 }, "error": null }
335
379
  ]
336
380
  }]
337
381
  }'
338
- # → 202 { "accepted": 2, "issuesDetected": 0 }
382
+ # → 202 { "accepted": 2, "rejected": 0, "duplicates": 0, "adjusted": 0, "errors": [], "issuesDetected": 0, ... }
339
383
  ```
340
384
 
341
385
  ## Logs (`POST /v1/logs`)
@@ -347,7 +391,7 @@ O SDK envia linhas de log para a plataforma em lote (`flushIntervalMs` 2 s, `max
347
391
  ```ts
348
392
  import { createLogShipper } from 'fg-tracker-sdk';
349
393
 
350
- const logs = createLogShipper({ apiKey: process.env.FG_TRACKER_KEY!, endpoint: 'http://localhost:4050', service: 'minha-api', env: 'production', tracker });
394
+ const logs = createLogShipper({ apiKey: process.env.FG_TRACKER_KEY!, endpoint: process.env.FG_TRACKER_URL, service: 'minha-api', env: 'production', tracker });
351
395
  logs.push({ level: 'info', message: 'user logged in', context: { userId: 42 }, requestId: req.id });
352
396
  logs.log('error', 'payment failed', { orderId });
353
397
  await logs.flush(); await logs.shutdown();
@@ -377,7 +421,7 @@ import { pinoTransport, traceMixin } from 'fg-tracker-sdk';
377
421
  const logger = pino({
378
422
  level: 'info',
379
423
  mixin: traceMixin(tracker), // opcional; correlação com traces
380
- transport: pinoTransport({ apiKey: process.env.FG_TRACKER_KEY!, endpoint: 'http://localhost:4050', service: 'minha-api', env: 'production' }),
424
+ transport: pinoTransport({ apiKey: process.env.FG_TRACKER_KEY!, endpoint: process.env.FG_TRACKER_URL, service: 'minha-api', env: 'production' }),
381
425
  // equivalente: transport: { target: 'fg-tracker-sdk/pino', options: { apiKey, service } }
382
426
  });
383
427
  app.use(pinoHttp({ logger })); // req.id vira requestId
@@ -397,7 +441,7 @@ const logger = winston.createLogger({
397
441
  format: winston.format.combine(winston.format.timestamp(), winston.format.json()),
398
442
  transports: [
399
443
  new winston.transports.Console(),
400
- new FgTrackerTransport({ apiKey: process.env.FG_TRACKER_KEY!, service: 'minha-api', tracker }),
444
+ new FgTrackerTransport({ apiKey: process.env.FG_TRACKER_KEY!, endpoint: process.env.FG_TRACKER_URL, service: 'minha-api', tracker }),
401
445
  ],
402
446
  });
403
447
  logger.warn('stock low', { sku: 'A-1' }); // meta → context
@@ -414,8 +458,8 @@ Implementa a forma do `LoggerService` sem depender de `@nestjs/common`: espelha
414
458
  import { NestFactory } from '@nestjs/core';
415
459
  import { createTracker, nestLogger } from 'fg-tracker-sdk';
416
460
 
417
- export const tracker = createTracker({ apiKey: process.env.FG_TRACKER_KEY!, serviceName: 'minha-api' });
418
- export const logger = nestLogger({ apiKey: process.env.FG_TRACKER_KEY!, service: 'minha-api', tracker });
461
+ export const tracker = createTracker({ apiKey: process.env.FG_TRACKER_KEY!, endpoint: process.env.FG_TRACKER_URL, serviceName: 'minha-api' });
462
+ export const logger = nestLogger({ apiKey: process.env.FG_TRACKER_KEY!, endpoint: process.env.FG_TRACKER_URL, service: 'minha-api', tracker });
419
463
 
420
464
  const app = await NestFactory.create(AppModule, { logger });
421
465
  app.use(tracker.express());
@@ -428,9 +472,9 @@ Dentro das classes continue usando `new Logger(MinhaClasse.name)` do Nest — el
428
472
  ### Ingestão crua de logs (qualquer linguagem)
429
473
 
430
474
  ```bash
431
- curl -X POST http://localhost:4050/v1/logs \\
432
- -H 'content-type: application/json' \\
433
- -H 'x-fg-tracker-key: stk_demo_0000000000000000000000000000' \\
475
+ curl -X POST "$FG_TRACKER_URL/v1/logs" \
476
+ -H 'content-type: application/json' \
477
+ -H "x-fg-tracker-key: $FG_TRACKER_KEY" \
434
478
  -d '{ "logs": [
435
479
  { "eventId": "9f1c2a7e4b0d4c4e8a51f0d2b3c4d5e6", "ts": "2026-09-12T12:00:00.000Z", "level": "info", "service": "minha-api", "message": "user logged in",
436
480
  "context": { "userId": 42 }, "traceId": "0123456789abcdef0123456789abcdef", "requestId": "req-1", "env": "production" },
@@ -587,7 +631,7 @@ O pacote foi renomeado de `stread-track-sdk` (0.3.0) para `fg-tracker-sdk` (0.4.
587
631
  | pino `target: 'stread-track-sdk/pino'` | `target: 'fg-tracker-sdk/pino'` | troque junto com o nome do pacote |
588
632
  | prefixo de log `[stread-track]` | `[fg-tracker]` | — |
589
633
 
590
- Passo a passo: `pnpm remove stread-track-sdk && pnpm add fg-tracker-sdk@^0.4.0`, troque os imports (`from 'stread-track-sdk'` → `from 'fg-tracker-sdk'`, inclusive `/pino` e `/winston`) e, quando quiser, renomeie as variáveis de ambiente e os bins no CI. A API precisa aceitar `x-fg-tracker-key` (versão da API a partir desta mudança) — a produção do FG Tracker já aceita.
634
+ Passo a passo: `pnpm remove stread-track-sdk && pnpm add fg-tracker-sdk@latest`, troque os imports (`from 'stread-track-sdk'` → `from 'fg-tracker-sdk'`, inclusive `/pino` e `/winston`) e, quando quiser, renomeie as variáveis de ambiente e os bins no CI. A API precisa aceitar `x-fg-tracker-key` (versão da API a partir desta mudança) — a produção do FG Tracker já aceita.
591
635
 
592
636
  ## Build
593
637
 
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAwCjE;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,GAAE,OAAO,UAAU,CAAC,KAAwB,GAAG,OAAO,UAAU,CAAC,KAAK,CA6BzI"}
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"AAOA,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAwCjE;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,GAAE,OAAO,UAAU,CAAC,KAAwB,GAAG,OAAO,UAAU,CAAC,KAAK,CAoDzI"}
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
- span.setError(err);
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
- span.end();
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;AAC1F,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,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,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,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,OAAO,GAAG,CAAC;QACb,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YACnB,MAAM,GAAG,CAAC;QACZ,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"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"}
@@ -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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"}