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.
- package/CHANGELOG.md +12 -0
- package/README.md +76 -32
- 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 +3 -0
- 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.map +1 -1
- package/dist/infra/aws.js +38 -2
- package/dist/infra/aws.js.map +1 -1
- package/dist/tracker.d.ts +9 -0
- package/dist/tracker.d.ts.map +1 -1
- package/dist/tracker.js +14 -0
- package/dist/tracker.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- 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** (
|
|
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!, //
|
|
26
|
-
endpoint:
|
|
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 =
|
|
69
|
-
| `scrubFields` | `DEFAULT_SCRUB_FIELDS` | chaves de atributos que viram `[Filtered]` (password, token, authorization, cookie,
|
|
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: '
|
|
102
|
-
// FG_TRACKER_KEY=
|
|
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=
|
|
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
|
|
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
|
-
|
|
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
|
|
366
|
+
-H "x-fg-tracker-key: $FG_TRACKER_KEY" \
|
|
323
367
|
-d '{
|
|
324
368
|
"traces": [{
|
|
325
|
-
"traceId": "
|
|
369
|
+
"traceId": "'"$TRACE_ID"'",
|
|
326
370
|
"spans": [
|
|
327
|
-
{ "spanId": "
|
|
371
|
+
{ "spanId": "'"$ROOT"'", "parentSpanId": null, "kind": "http.server", "name": "GET /users/:id",
|
|
328
372
|
"method": "GET", "route": "/users/:id", "statusCode": 200,
|
|
329
|
-
"startedAt": "
|
|
330
|
-
"attributes": { "url": "/users/42" }, "error": null },
|
|
331
|
-
{ "spanId": "
|
|
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": "
|
|
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:
|
|
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:
|
|
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
|
|
432
|
-
-H 'content-type: application/json'
|
|
433
|
-
-H
|
|
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
|
|
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
|
|
package/dist/fetch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"
|
|
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
|
-
|
|
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":"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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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"}
|