@moduna/otel 1.0.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/LICENSE +21 -0
- package/README.md +130 -0
- package/dist/interface/ModunaOTELConfig.d.ts +41 -0
- package/dist/interface/ModunaOTELConfig.js +1 -0
- package/dist/services/ModunaLangChainCallbackHandler.d.ts +90 -0
- package/dist/services/ModunaLangChainCallbackHandler.js +191 -0
- package/dist/services/ModunaOTEL.d.ts +92 -0
- package/dist/services/ModunaOTEL.js +243 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.js +2 -0
- package/dist/types/SupportedSDK.d.ts +8 -0
- package/dist/types/SupportedSDK.js +1 -0
- package/dist/types/TraceCallback.d.ts +2 -0
- package/dist/types/TraceCallback.js +1 -0
- package/dist/types/TraceContext.d.ts +21 -0
- package/dist/types/TraceContext.js +1 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Moduna
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# @moduna/otel
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@moduna/otel)
|
|
4
|
+
[](https://www.npmjs.com/package/@moduna/otel)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://opentelemetry.io/)
|
|
8
|
+
[](https://pnpm.io/)
|
|
9
|
+
[](https://github.com/Moduna-AI/node-otel-sdk/stargazers)
|
|
10
|
+
|
|
11
|
+
OpenTelemetry setup for Moduna AI traces in Node.js apps, including Vercel AI SDK and LangChain.
|
|
12
|
+
|
|
13
|
+
## Project Health
|
|
14
|
+
|
|
15
|
+
| Metric | Status |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| Package | [`@moduna/otel`](https://www.npmjs.com/package/@moduna/otel) |
|
|
18
|
+
| Runtime | Node.js ESM |
|
|
19
|
+
| Types | TypeScript declarations included |
|
|
20
|
+
| License | MIT |
|
|
21
|
+
| Package managers | npm, pnpm, bun |
|
|
22
|
+
| Frameworks | Vercel AI SDK, LangChain |
|
|
23
|
+
| Telemetry | OpenTelemetry traces |
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @moduna/otel
|
|
29
|
+
pnpm add @moduna/otel
|
|
30
|
+
bun add @moduna/otel
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Setup
|
|
34
|
+
|
|
35
|
+
Set the Moduna key using either a shell or a `.env` file:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
export MODUNA_API_KEY="your-moduna-key"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
or create a `.env` file with:
|
|
42
|
+
|
|
43
|
+
```env
|
|
44
|
+
MODUNA_API_KEY=your-moduna-key
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## One-line integration
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import ModunaOTEL from "@moduna/otel";
|
|
51
|
+
|
|
52
|
+
const otel = new ModunaOTEL({
|
|
53
|
+
agentName: "my-ai-app",
|
|
54
|
+
framework: "vercel-ai-sdk",
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Framework Usage
|
|
59
|
+
|
|
60
|
+
Choose the framework you are using with Moduna OTEL.
|
|
61
|
+
|
|
62
|
+
<details open>
|
|
63
|
+
<summary><strong>Vercel AI SDK</strong></summary>
|
|
64
|
+
|
|
65
|
+
Pass per-call conversation or session identifiers to `generateText` and `streamText`.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const result = await generateText({
|
|
69
|
+
model,
|
|
70
|
+
prompt,
|
|
71
|
+
experimental_telemetry: otel.vercelTelemetry({
|
|
72
|
+
conversationId: "conversation-123",
|
|
73
|
+
sessionId: "session-456",
|
|
74
|
+
}),
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The IDs are attached to the generated spans as Moduna telemetry metadata.
|
|
79
|
+
|
|
80
|
+
</details>
|
|
81
|
+
|
|
82
|
+
<details>
|
|
83
|
+
<summary><strong>LangChain</strong></summary>
|
|
84
|
+
|
|
85
|
+
Use Moduna as a LangChain callback handler.
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import ModunaOTEL from "@moduna/otel";
|
|
89
|
+
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
|
|
90
|
+
|
|
91
|
+
const otel = new ModunaOTEL({
|
|
92
|
+
agentName: "my-ai-app",
|
|
93
|
+
framework: "langchain",
|
|
94
|
+
});
|
|
95
|
+
const handler = otel.langChainHandler();
|
|
96
|
+
const model = new ChatGoogleGenerativeAI({ model: "gemini-1.5-pro" });
|
|
97
|
+
|
|
98
|
+
const result = await model.invoke("Hello, world!", {
|
|
99
|
+
callbacks: [handler],
|
|
100
|
+
metadata: {
|
|
101
|
+
conversationId: "conversation-123",
|
|
102
|
+
sessionId: "session-456",
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Or register it globally for all LangChain runs.
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
otel.registerGlobalLangChainHandler();
|
|
111
|
+
|
|
112
|
+
const result = await model.invoke("Hello, world!", {
|
|
113
|
+
metadata: {
|
|
114
|
+
conversationId: "conversation-123",
|
|
115
|
+
sessionId: "session-456",
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
</details>
|
|
121
|
+
|
|
122
|
+
## Development
|
|
123
|
+
|
|
124
|
+
Format, lint, or build with:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
pnpm run build
|
|
128
|
+
pnpm run lint
|
|
129
|
+
pnpm test
|
|
130
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ModunaOTELFramework, ModunaOTELSDKIntegration } from "../types/SupportedSDK.js";
|
|
2
|
+
/**
|
|
3
|
+
* Shared configuration for the Moduna OpenTelemetry SDK.
|
|
4
|
+
*/
|
|
5
|
+
interface ModunaOTELBaseConfig {
|
|
6
|
+
/**
|
|
7
|
+
* API key for authenticating with Moduna. Falls back to MODUNA_API_KEY.
|
|
8
|
+
*/
|
|
9
|
+
apiKey?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Service name attached to emitted OpenTelemetry resources.
|
|
12
|
+
*/
|
|
13
|
+
agentName: string;
|
|
14
|
+
/**
|
|
15
|
+
* Additional OTLP headers sent with telemetry exports.
|
|
16
|
+
*/
|
|
17
|
+
headers?: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Configuration options for a ModunaOTEL instance.
|
|
21
|
+
*/
|
|
22
|
+
export type ModunaOTELConfig = ModunaOTELBaseConfig & ({
|
|
23
|
+
/**
|
|
24
|
+
* Framework that will use the Moduna OpenTelemetry SDK.
|
|
25
|
+
*/
|
|
26
|
+
framework: ModunaOTELFramework;
|
|
27
|
+
/**
|
|
28
|
+
* Deprecated. Use framework instead.
|
|
29
|
+
*/
|
|
30
|
+
sdkIntegration?: ModunaOTELSDKIntegration;
|
|
31
|
+
} | {
|
|
32
|
+
/**
|
|
33
|
+
* Framework that will use the Moduna OpenTelemetry SDK.
|
|
34
|
+
*/
|
|
35
|
+
framework?: ModunaOTELFramework;
|
|
36
|
+
/**
|
|
37
|
+
* Deprecated. Use framework instead.
|
|
38
|
+
*/
|
|
39
|
+
sdkIntegration: ModunaOTELSDKIntegration;
|
|
40
|
+
});
|
|
41
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
|
|
2
|
+
import type { Serialized } from "@langchain/core/load/serializable";
|
|
3
|
+
import type { BaseMessage } from "@langchain/core/messages";
|
|
4
|
+
import type { LLMResult } from "@langchain/core/outputs";
|
|
5
|
+
import type { ModunaTraceContext } from "../types/TraceContext.js";
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for Moduna's LangChain callback handler.
|
|
8
|
+
*/
|
|
9
|
+
export interface ModunaLangChainCallbackHandlerConfig {
|
|
10
|
+
/**
|
|
11
|
+
* Default trace identifiers used when a LangChain call has no metadata.
|
|
12
|
+
*/
|
|
13
|
+
traceContext?: ModunaTraceContext;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* LangChain callback handler that emits Moduna OpenTelemetry spans.
|
|
17
|
+
*/
|
|
18
|
+
export declare class ModunaLangChainCallbackHandler extends BaseCallbackHandler {
|
|
19
|
+
/**
|
|
20
|
+
* Stable handler name used by LangChain callback manager de-duplication.
|
|
21
|
+
*/
|
|
22
|
+
name: string;
|
|
23
|
+
private readonly defaultTraceContext;
|
|
24
|
+
private readonly runs;
|
|
25
|
+
/**
|
|
26
|
+
* Creates a LangChain callback handler for Moduna spans.
|
|
27
|
+
*
|
|
28
|
+
* @param config Optional default trace identifiers.
|
|
29
|
+
*/
|
|
30
|
+
constructor(config?: ModunaLangChainCallbackHandlerConfig);
|
|
31
|
+
/**
|
|
32
|
+
* Starts a span for a LangChain chat model run.
|
|
33
|
+
*
|
|
34
|
+
* @param llm Serialized LangChain model metadata.
|
|
35
|
+
* @param messages Messages sent to the chat model.
|
|
36
|
+
* @param runId LangChain run identifier.
|
|
37
|
+
* @param parentRunId Parent run identifier, when present.
|
|
38
|
+
* @param extraParams Provider-specific run parameters.
|
|
39
|
+
* @param tags LangChain run tags.
|
|
40
|
+
* @param metadata LangChain run metadata.
|
|
41
|
+
* @param runName LangChain run name.
|
|
42
|
+
*/
|
|
43
|
+
handleChatModelStart(llm: Serialized, messages: BaseMessage[][], runId: string, parentRunId?: string, extraParams?: Record<string, unknown>, tags?: string[], metadata?: Record<string, unknown>, runName?: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Starts a span for a LangChain LLM run.
|
|
46
|
+
*
|
|
47
|
+
* @param llm Serialized LangChain model metadata.
|
|
48
|
+
* @param prompts Prompts sent to the LLM.
|
|
49
|
+
* @param runId LangChain run identifier.
|
|
50
|
+
* @param parentRunId Parent run identifier, when present.
|
|
51
|
+
* @param extraParams Provider-specific run parameters.
|
|
52
|
+
* @param tags LangChain run tags.
|
|
53
|
+
* @param metadata LangChain run metadata.
|
|
54
|
+
* @param runName LangChain run name.
|
|
55
|
+
*/
|
|
56
|
+
handleLLMStart(llm: Serialized, prompts: string[], runId: string, parentRunId?: string, extraParams?: Record<string, unknown>, tags?: string[], metadata?: Record<string, unknown>, runName?: string): void;
|
|
57
|
+
/**
|
|
58
|
+
* Records a streamed token count signal on the active LangChain span.
|
|
59
|
+
*
|
|
60
|
+
* @param _token New token emitted by LangChain.
|
|
61
|
+
* @param _idx Token indices from LangChain.
|
|
62
|
+
* @param runId LangChain run identifier.
|
|
63
|
+
*/
|
|
64
|
+
handleLLMNewToken(_token: string, _idx: unknown, runId: string): void;
|
|
65
|
+
/**
|
|
66
|
+
* Ends the span for a successful LangChain run.
|
|
67
|
+
*
|
|
68
|
+
* @param output LangChain LLM output.
|
|
69
|
+
* @param runId LangChain run identifier.
|
|
70
|
+
*/
|
|
71
|
+
handleLLMEnd(output: LLMResult, runId: string): void;
|
|
72
|
+
/**
|
|
73
|
+
* Records an error and ends the span for a failed LangChain run.
|
|
74
|
+
*
|
|
75
|
+
* @param error Error emitted by LangChain.
|
|
76
|
+
* @param runId LangChain run identifier.
|
|
77
|
+
*/
|
|
78
|
+
handleLLMError(error: unknown, runId: string): void;
|
|
79
|
+
private startRun;
|
|
80
|
+
private applyModelAttributes;
|
|
81
|
+
private applyTraceContext;
|
|
82
|
+
private getString;
|
|
83
|
+
private toError;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Registers a Moduna LangChain callback handler for all LangChain runs.
|
|
87
|
+
*
|
|
88
|
+
* @param handler Handler to register globally.
|
|
89
|
+
*/
|
|
90
|
+
export declare const registerGlobalModunaLangChainHandler: (handler: ModunaLangChainCallbackHandler) => void;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
2
|
+
import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
|
|
3
|
+
import { registerConfigureHook, setContextVariable } from "@langchain/core/context";
|
|
4
|
+
const MODUNA_LANGCHAIN_HANDLER_CONTEXT_KEY = "moduna.otel.langchain.callbackHandler";
|
|
5
|
+
let isConfigureHookRegistered = false;
|
|
6
|
+
/**
|
|
7
|
+
* LangChain callback handler that emits Moduna OpenTelemetry spans.
|
|
8
|
+
*/
|
|
9
|
+
export class ModunaLangChainCallbackHandler extends BaseCallbackHandler {
|
|
10
|
+
/**
|
|
11
|
+
* Stable handler name used by LangChain callback manager de-duplication.
|
|
12
|
+
*/
|
|
13
|
+
name = "moduna_otel_langchain_callback_handler";
|
|
14
|
+
defaultTraceContext;
|
|
15
|
+
runs = new Map();
|
|
16
|
+
/**
|
|
17
|
+
* Creates a LangChain callback handler for Moduna spans.
|
|
18
|
+
*
|
|
19
|
+
* @param config Optional default trace identifiers.
|
|
20
|
+
*/
|
|
21
|
+
constructor(config = {}) {
|
|
22
|
+
super();
|
|
23
|
+
this.defaultTraceContext = config.traceContext ?? {};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Starts a span for a LangChain chat model run.
|
|
27
|
+
*
|
|
28
|
+
* @param llm Serialized LangChain model metadata.
|
|
29
|
+
* @param messages Messages sent to the chat model.
|
|
30
|
+
* @param runId LangChain run identifier.
|
|
31
|
+
* @param parentRunId Parent run identifier, when present.
|
|
32
|
+
* @param extraParams Provider-specific run parameters.
|
|
33
|
+
* @param tags LangChain run tags.
|
|
34
|
+
* @param metadata LangChain run metadata.
|
|
35
|
+
* @param runName LangChain run name.
|
|
36
|
+
*/
|
|
37
|
+
handleChatModelStart(llm, messages, runId, parentRunId, extraParams, tags, metadata, runName) {
|
|
38
|
+
this.startRun({
|
|
39
|
+
extraParams,
|
|
40
|
+
inputCount: messages.length,
|
|
41
|
+
llm,
|
|
42
|
+
metadata,
|
|
43
|
+
parentRunId,
|
|
44
|
+
runId,
|
|
45
|
+
runName,
|
|
46
|
+
runType: "chat_model",
|
|
47
|
+
tags,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Starts a span for a LangChain LLM run.
|
|
52
|
+
*
|
|
53
|
+
* @param llm Serialized LangChain model metadata.
|
|
54
|
+
* @param prompts Prompts sent to the LLM.
|
|
55
|
+
* @param runId LangChain run identifier.
|
|
56
|
+
* @param parentRunId Parent run identifier, when present.
|
|
57
|
+
* @param extraParams Provider-specific run parameters.
|
|
58
|
+
* @param tags LangChain run tags.
|
|
59
|
+
* @param metadata LangChain run metadata.
|
|
60
|
+
* @param runName LangChain run name.
|
|
61
|
+
*/
|
|
62
|
+
handleLLMStart(llm, prompts, runId, parentRunId, extraParams, tags, metadata, runName) {
|
|
63
|
+
this.startRun({
|
|
64
|
+
extraParams,
|
|
65
|
+
inputCount: prompts.length,
|
|
66
|
+
llm,
|
|
67
|
+
metadata,
|
|
68
|
+
parentRunId,
|
|
69
|
+
runId,
|
|
70
|
+
runName,
|
|
71
|
+
runType: "llm",
|
|
72
|
+
tags,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Records a streamed token count signal on the active LangChain span.
|
|
77
|
+
*
|
|
78
|
+
* @param _token New token emitted by LangChain.
|
|
79
|
+
* @param _idx Token indices from LangChain.
|
|
80
|
+
* @param runId LangChain run identifier.
|
|
81
|
+
*/
|
|
82
|
+
handleLLMNewToken(_token, _idx, runId) {
|
|
83
|
+
this.runs.get(runId)?.span.addEvent("langchain.llm.token");
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Ends the span for a successful LangChain run.
|
|
87
|
+
*
|
|
88
|
+
* @param output LangChain LLM output.
|
|
89
|
+
* @param runId LangChain run identifier.
|
|
90
|
+
*/
|
|
91
|
+
handleLLMEnd(output, runId) {
|
|
92
|
+
const run = this.runs.get(runId);
|
|
93
|
+
if (!run) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
run.span.setAttribute("langchain.output.generations", output.generations.length);
|
|
97
|
+
run.span.setStatus({ code: SpanStatusCode.OK });
|
|
98
|
+
run.span.end();
|
|
99
|
+
this.runs.delete(runId);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Records an error and ends the span for a failed LangChain run.
|
|
103
|
+
*
|
|
104
|
+
* @param error Error emitted by LangChain.
|
|
105
|
+
* @param runId LangChain run identifier.
|
|
106
|
+
*/
|
|
107
|
+
handleLLMError(error, runId) {
|
|
108
|
+
const run = this.runs.get(runId);
|
|
109
|
+
if (!run) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
run.span.recordException(this.toError(error));
|
|
113
|
+
run.span.setStatus({
|
|
114
|
+
code: SpanStatusCode.ERROR,
|
|
115
|
+
message: this.toError(error).message,
|
|
116
|
+
});
|
|
117
|
+
run.span.end();
|
|
118
|
+
this.runs.delete(runId);
|
|
119
|
+
}
|
|
120
|
+
startRun(input) {
|
|
121
|
+
const tracer = trace.getTracer("moduna-langchain");
|
|
122
|
+
const span = tracer.startSpan(input.runName ?? "langchain.llm", {
|
|
123
|
+
kind: SpanKind.CLIENT,
|
|
124
|
+
});
|
|
125
|
+
span.setAttribute("moduna.framework", "langchain");
|
|
126
|
+
span.setAttribute("sdk.integration", "langchain");
|
|
127
|
+
span.setAttribute("langchain.run.id", input.runId);
|
|
128
|
+
span.setAttribute("langchain.run.type", input.runType);
|
|
129
|
+
span.setAttribute("langchain.input.count", input.inputCount);
|
|
130
|
+
if (input.parentRunId) {
|
|
131
|
+
span.setAttribute("langchain.parent_run.id", input.parentRunId);
|
|
132
|
+
}
|
|
133
|
+
if (input.tags?.length) {
|
|
134
|
+
span.setAttribute("langchain.tags", input.tags.join(","));
|
|
135
|
+
}
|
|
136
|
+
this.applyModelAttributes(span, input.llm, input.extraParams);
|
|
137
|
+
this.applyTraceContext(span, input.metadata);
|
|
138
|
+
this.runs.set(input.runId, { span });
|
|
139
|
+
}
|
|
140
|
+
applyModelAttributes(span, llm, extraParams) {
|
|
141
|
+
const modelName = this.getString(extraParams?.invocation_params, "model") ??
|
|
142
|
+
this.getString(extraParams?.invocation_params, "modelName") ??
|
|
143
|
+
this.getString(llm, "name") ??
|
|
144
|
+
llm.id.join(".");
|
|
145
|
+
span.setAttribute("gen_ai.system", "google");
|
|
146
|
+
span.setAttribute("gen_ai.request.model", modelName);
|
|
147
|
+
span.setAttribute("langchain.serialized.id", llm.id.join("."));
|
|
148
|
+
}
|
|
149
|
+
applyTraceContext(span, metadata) {
|
|
150
|
+
const traceContext = {
|
|
151
|
+
conversationId: this.getString(metadata, "conversationId") ??
|
|
152
|
+
this.getString(metadata, "moduna.conversation.id") ??
|
|
153
|
+
this.defaultTraceContext.conversationId,
|
|
154
|
+
sessionId: this.getString(metadata, "sessionId") ??
|
|
155
|
+
this.getString(metadata, "moduna.session.id") ??
|
|
156
|
+
this.defaultTraceContext.sessionId,
|
|
157
|
+
};
|
|
158
|
+
if (traceContext.conversationId) {
|
|
159
|
+
span.setAttribute("moduna.conversation.id", traceContext.conversationId);
|
|
160
|
+
}
|
|
161
|
+
if (traceContext.sessionId) {
|
|
162
|
+
span.setAttribute("moduna.session.id", traceContext.sessionId);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
getString(value, key) {
|
|
166
|
+
if (!value || typeof value !== "object") {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
const record = value;
|
|
170
|
+
const result = record[key];
|
|
171
|
+
return typeof result === "string" ? result : undefined;
|
|
172
|
+
}
|
|
173
|
+
toError(error) {
|
|
174
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Registers a Moduna LangChain callback handler for all LangChain runs.
|
|
179
|
+
*
|
|
180
|
+
* @param handler Handler to register globally.
|
|
181
|
+
*/
|
|
182
|
+
export const registerGlobalModunaLangChainHandler = (handler) => {
|
|
183
|
+
if (!isConfigureHookRegistered) {
|
|
184
|
+
registerConfigureHook({
|
|
185
|
+
contextVar: MODUNA_LANGCHAIN_HANDLER_CONTEXT_KEY,
|
|
186
|
+
inheritable: true,
|
|
187
|
+
});
|
|
188
|
+
isConfigureHookRegistered = true;
|
|
189
|
+
}
|
|
190
|
+
setContextVariable(MODUNA_LANGCHAIN_HANDLER_CONTEXT_KEY, handler);
|
|
191
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { AttributeValue } from "@opentelemetry/api";
|
|
2
|
+
import type { ModunaOTELConfig } from "../interface/ModunaOTELConfig.js";
|
|
3
|
+
import type { TraceCallback } from "../types/TraceCallback.js";
|
|
4
|
+
import type { ModunaTelemetryMetadata, ModunaTraceContext } from "../types/TraceContext.js";
|
|
5
|
+
import { ModunaLangChainCallbackHandler } from "./ModunaLangChainCallbackHandler.js";
|
|
6
|
+
/**
|
|
7
|
+
* Vercel AI SDK compatible telemetry settings.
|
|
8
|
+
*/
|
|
9
|
+
export interface ModunaVercelTelemetrySettings {
|
|
10
|
+
/**
|
|
11
|
+
* Enables Vercel AI SDK telemetry for the current model call.
|
|
12
|
+
*/
|
|
13
|
+
isEnabled: true;
|
|
14
|
+
/**
|
|
15
|
+
* Per-call Moduna metadata attached to the generated OpenTelemetry spans.
|
|
16
|
+
*/
|
|
17
|
+
metadata: ModunaTelemetryMetadata & Record<string, AttributeValue>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* One-line OpenTelemetry setup for Moduna AI traces.
|
|
21
|
+
*/
|
|
22
|
+
export declare class ModunaOTEL {
|
|
23
|
+
private static readonly shared;
|
|
24
|
+
private readonly framework;
|
|
25
|
+
/**
|
|
26
|
+
* Creates a Moduna OTEL wrapper and starts telemetry asynchronously.
|
|
27
|
+
*
|
|
28
|
+
* @param config SDK configuration for the current application.
|
|
29
|
+
*/
|
|
30
|
+
constructor(config: ModunaOTELConfig);
|
|
31
|
+
/**
|
|
32
|
+
* Creates and starts a ModunaOTEL instance.
|
|
33
|
+
*
|
|
34
|
+
* @param config SDK configuration for the current application.
|
|
35
|
+
* @returns The started ModunaOTEL wrapper.
|
|
36
|
+
*/
|
|
37
|
+
static start(config: ModunaOTELConfig): Promise<ModunaOTEL>;
|
|
38
|
+
/**
|
|
39
|
+
* Starts the singleton OpenTelemetry SDK.
|
|
40
|
+
*/
|
|
41
|
+
start(): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Shuts down the singleton OpenTelemetry SDK.
|
|
44
|
+
*/
|
|
45
|
+
shutdown(): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Creates telemetry settings for one Vercel AI SDK generateText or streamText call.
|
|
48
|
+
*
|
|
49
|
+
* @param context Conversation or session identifiers for the current AI call.
|
|
50
|
+
* @returns Vercel AI SDK experimental telemetry settings.
|
|
51
|
+
*/
|
|
52
|
+
vercelTelemetry(context?: ModunaTraceContext): ModunaVercelTelemetrySettings;
|
|
53
|
+
/**
|
|
54
|
+
* Creates a LangChain callback handler for per-call usage.
|
|
55
|
+
*
|
|
56
|
+
* @param context Default conversation or session identifiers.
|
|
57
|
+
* @returns LangChain callback handler that emits Moduna spans.
|
|
58
|
+
*/
|
|
59
|
+
langChainHandler(context?: ModunaTraceContext): ModunaLangChainCallbackHandler;
|
|
60
|
+
/**
|
|
61
|
+
* Registers a LangChain callback handler for all LangChain runs.
|
|
62
|
+
*
|
|
63
|
+
* @param context Default conversation or session identifiers.
|
|
64
|
+
* @returns The globally registered LangChain callback handler.
|
|
65
|
+
*/
|
|
66
|
+
registerGlobalLangChainHandler(context?: ModunaTraceContext): ModunaLangChainCallbackHandler;
|
|
67
|
+
/**
|
|
68
|
+
* Instruments a callback with a Moduna span.
|
|
69
|
+
*
|
|
70
|
+
* @param spanName Name for the emitted span.
|
|
71
|
+
* @param callback Callback executed inside the active span.
|
|
72
|
+
* @returns The callback result.
|
|
73
|
+
*/
|
|
74
|
+
instrument<T>(spanName: string, callback: TraceCallback<T>): Promise<T>;
|
|
75
|
+
/**
|
|
76
|
+
* Instruments a callback with a Moduna span and per-call trace context.
|
|
77
|
+
*
|
|
78
|
+
* @param spanName Name for the emitted span.
|
|
79
|
+
* @param context Conversation or session identifiers for the current AI call.
|
|
80
|
+
* @param callback Callback executed inside the active span.
|
|
81
|
+
* @returns The callback result.
|
|
82
|
+
*/
|
|
83
|
+
instrument<T>(spanName: string, context: ModunaTraceContext, callback: TraceCallback<T>): Promise<T>;
|
|
84
|
+
private normalizeConfig;
|
|
85
|
+
private createSDK;
|
|
86
|
+
private startSafely;
|
|
87
|
+
private createTraceMetadata;
|
|
88
|
+
private applyTraceContext;
|
|
89
|
+
private parseInstrumentArgs;
|
|
90
|
+
private warnOnce;
|
|
91
|
+
}
|
|
92
|
+
export default ModunaOTEL;
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { SpanKind, trace } from "@opentelemetry/api";
|
|
3
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
4
|
+
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
5
|
+
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
6
|
+
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
|
|
7
|
+
import { ModunaLangChainCallbackHandler, registerGlobalModunaLangChainHandler, } from "./ModunaLangChainCallbackHandler.js";
|
|
8
|
+
const DEFAULT_ENDPOINT = "https://volex-otel-git-506013021984.us-central1.run.app/v1/traces";
|
|
9
|
+
class SilentOTLPTraceExporter extends OTLPTraceExporter {
|
|
10
|
+
onFailure;
|
|
11
|
+
/**
|
|
12
|
+
* Creates an OTLP exporter that reports failures without breaking user code.
|
|
13
|
+
*
|
|
14
|
+
* @param config OTLP HTTP exporter configuration.
|
|
15
|
+
* @param onFailure Callback invoked when export fails.
|
|
16
|
+
*/
|
|
17
|
+
constructor(config, onFailure) {
|
|
18
|
+
super(config);
|
|
19
|
+
this.onFailure = onFailure;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Exports spans and converts synchronous exporter failures into callback results.
|
|
23
|
+
*
|
|
24
|
+
* @param spans Readable spans from the OpenTelemetry processor.
|
|
25
|
+
* @param resultCallback OpenTelemetry export completion callback.
|
|
26
|
+
*/
|
|
27
|
+
export(spans, resultCallback) {
|
|
28
|
+
try {
|
|
29
|
+
return super.export(spans, (result) => {
|
|
30
|
+
if (result.error || result.code !== 0) {
|
|
31
|
+
this.onFailure(result.error ??
|
|
32
|
+
new Error(`Moduna OTEL exporter failed with code ${result.code}.`));
|
|
33
|
+
}
|
|
34
|
+
resultCallback(result);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
this.onFailure(error);
|
|
39
|
+
resultCallback(this.createFailureResult(error));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
createFailureResult(error) {
|
|
43
|
+
return {
|
|
44
|
+
code: 1,
|
|
45
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* One-line OpenTelemetry setup for Moduna AI traces.
|
|
51
|
+
*/
|
|
52
|
+
export class ModunaOTEL {
|
|
53
|
+
static shared = {
|
|
54
|
+
started: false,
|
|
55
|
+
warned: false,
|
|
56
|
+
};
|
|
57
|
+
framework;
|
|
58
|
+
/**
|
|
59
|
+
* Creates a Moduna OTEL wrapper and starts telemetry asynchronously.
|
|
60
|
+
*
|
|
61
|
+
* @param config SDK configuration for the current application.
|
|
62
|
+
*/
|
|
63
|
+
constructor(config) {
|
|
64
|
+
const normalizedConfig = this.normalizeConfig(config);
|
|
65
|
+
this.framework = normalizedConfig.framework;
|
|
66
|
+
ModunaOTEL.shared.sdk ??= this.createSDK(normalizedConfig);
|
|
67
|
+
void this.start();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Creates and starts a ModunaOTEL instance.
|
|
71
|
+
*
|
|
72
|
+
* @param config SDK configuration for the current application.
|
|
73
|
+
* @returns The started ModunaOTEL wrapper.
|
|
74
|
+
*/
|
|
75
|
+
static async start(config) {
|
|
76
|
+
const otel = new ModunaOTEL(config);
|
|
77
|
+
await otel.start();
|
|
78
|
+
return otel;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Starts the singleton OpenTelemetry SDK.
|
|
82
|
+
*/
|
|
83
|
+
async start() {
|
|
84
|
+
if (ModunaOTEL.shared.started) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
ModunaOTEL.shared.startPromise ??= this.startSafely();
|
|
88
|
+
await ModunaOTEL.shared.startPromise;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Shuts down the singleton OpenTelemetry SDK.
|
|
92
|
+
*/
|
|
93
|
+
async shutdown() {
|
|
94
|
+
const sdk = ModunaOTEL.shared.sdk;
|
|
95
|
+
if (!ModunaOTEL.shared.started || !sdk) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
await sdk.shutdown();
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
this.warnOnce(error);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
ModunaOTEL.shared.started = false;
|
|
106
|
+
ModunaOTEL.shared.startPromise = undefined;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Creates telemetry settings for one Vercel AI SDK generateText or streamText call.
|
|
111
|
+
*
|
|
112
|
+
* @param context Conversation or session identifiers for the current AI call.
|
|
113
|
+
* @returns Vercel AI SDK experimental telemetry settings.
|
|
114
|
+
*/
|
|
115
|
+
vercelTelemetry(context = {}) {
|
|
116
|
+
return {
|
|
117
|
+
isEnabled: true,
|
|
118
|
+
metadata: this.createTraceMetadata(context),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Creates a LangChain callback handler for per-call usage.
|
|
123
|
+
*
|
|
124
|
+
* @param context Default conversation or session identifiers.
|
|
125
|
+
* @returns LangChain callback handler that emits Moduna spans.
|
|
126
|
+
*/
|
|
127
|
+
langChainHandler(context = {}) {
|
|
128
|
+
return new ModunaLangChainCallbackHandler({
|
|
129
|
+
traceContext: context,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Registers a LangChain callback handler for all LangChain runs.
|
|
134
|
+
*
|
|
135
|
+
* @param context Default conversation or session identifiers.
|
|
136
|
+
* @returns The globally registered LangChain callback handler.
|
|
137
|
+
*/
|
|
138
|
+
registerGlobalLangChainHandler(context = {}) {
|
|
139
|
+
const handler = this.langChainHandler(context);
|
|
140
|
+
registerGlobalModunaLangChainHandler(handler);
|
|
141
|
+
return handler;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Instruments a callback with optional Moduna trace context.
|
|
145
|
+
*/
|
|
146
|
+
async instrument(spanName, contextOrCallback, callback) {
|
|
147
|
+
const { traceContext, traceCallback } = this.parseInstrumentArgs(contextOrCallback, callback);
|
|
148
|
+
const tracer = trace.getTracer("moduna-gen-ai");
|
|
149
|
+
return tracer.startActiveSpan(spanName, { kind: SpanKind.CLIENT }, async (span) => {
|
|
150
|
+
span.setAttribute("moduna.framework", this.framework);
|
|
151
|
+
span.setAttribute("sdk.integration", this.framework);
|
|
152
|
+
this.applyTraceContext(span, traceContext);
|
|
153
|
+
try {
|
|
154
|
+
return await traceCallback(span);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
span.recordException(error);
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
span.end();
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
normalizeConfig(config) {
|
|
166
|
+
return {
|
|
167
|
+
agentName: config.agentName,
|
|
168
|
+
apiKey: config.apiKey ?? process.env.MODUNA_API_KEY,
|
|
169
|
+
framework: config.framework ?? config.sdkIntegration,
|
|
170
|
+
headers: config.headers,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
createSDK(config) {
|
|
174
|
+
return new NodeSDK({
|
|
175
|
+
resource: resourceFromAttributes({
|
|
176
|
+
[ATTR_SERVICE_NAME]: config.agentName,
|
|
177
|
+
"moduna.framework": config.framework,
|
|
178
|
+
"sdk.integration": config.framework,
|
|
179
|
+
}),
|
|
180
|
+
traceExporter: new SilentOTLPTraceExporter({
|
|
181
|
+
url: DEFAULT_ENDPOINT,
|
|
182
|
+
headers: {
|
|
183
|
+
...(config.apiKey
|
|
184
|
+
? { Authorization: `Bearer ${config.apiKey}` }
|
|
185
|
+
: {}),
|
|
186
|
+
...config.headers,
|
|
187
|
+
},
|
|
188
|
+
}, (error) => this.warnOnce(error)),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
async startSafely() {
|
|
192
|
+
const sdk = ModunaOTEL.shared.sdk;
|
|
193
|
+
if (!sdk) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
await Promise.resolve(sdk.start());
|
|
198
|
+
ModunaOTEL.shared.started = true;
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
this.warnOnce(error);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
createTraceMetadata(context) {
|
|
205
|
+
return {
|
|
206
|
+
...(context.conversationId
|
|
207
|
+
? { "moduna.conversation.id": context.conversationId }
|
|
208
|
+
: {}),
|
|
209
|
+
...(context.sessionId
|
|
210
|
+
? { "moduna.session.id": context.sessionId }
|
|
211
|
+
: {}),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
applyTraceContext(span, context) {
|
|
215
|
+
const metadata = this.createTraceMetadata(context);
|
|
216
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
217
|
+
span.setAttribute(key, value);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
parseInstrumentArgs(contextOrCallback, callback) {
|
|
221
|
+
if (typeof contextOrCallback === "function") {
|
|
222
|
+
return {
|
|
223
|
+
traceContext: {},
|
|
224
|
+
traceCallback: contextOrCallback,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
if (!callback) {
|
|
228
|
+
throw new TypeError("ModunaOTEL.instrument requires a callback.");
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
traceContext: contextOrCallback,
|
|
232
|
+
traceCallback: callback,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
warnOnce(error) {
|
|
236
|
+
if (ModunaOTEL.shared.warned) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
ModunaOTEL.shared.warned = true;
|
|
240
|
+
console.warn("Moduna OTEL failed to send telemetry.", error);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
export default ModunaOTEL;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { default, ModunaOTEL } from "../services/ModunaOTEL.js";
|
|
2
|
+
export { ModunaLangChainCallbackHandler, registerGlobalModunaLangChainHandler, } from "../services/ModunaLangChainCallbackHandler.js";
|
|
3
|
+
export type { ModunaLangChainCallbackHandlerConfig } from "../services/ModunaLangChainCallbackHandler.js";
|
|
4
|
+
export type { ModunaVercelTelemetrySettings } from "../services/ModunaOTEL.js";
|
|
5
|
+
export type { ModunaOTELConfig } from "../interface/ModunaOTELConfig.js";
|
|
6
|
+
export type { ModunaOTELFramework, ModunaOTELSDKIntegration, } from "../types/SupportedSDK.js";
|
|
7
|
+
export type { TraceCallback } from "../types/TraceCallback.js";
|
|
8
|
+
export type { ModunaTelemetryMetadata, ModunaTelemetryMetadataKey, ModunaTraceContext, } from "../types/TraceContext.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frameworks supported by the Moduna OpenTelemetry SDK.
|
|
3
|
+
*/
|
|
4
|
+
export type ModunaOTELFramework = "langchain" | "vercel-ai-sdk";
|
|
5
|
+
/**
|
|
6
|
+
* Deprecated alias kept for existing consumers that still pass sdkIntegration.
|
|
7
|
+
*/
|
|
8
|
+
export type ModunaOTELSDKIntegration = ModunaOTELFramework;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-call identifiers that are attached to Moduna trace spans.
|
|
3
|
+
*/
|
|
4
|
+
export interface ModunaTraceContext {
|
|
5
|
+
/**
|
|
6
|
+
* Identifier for a multi-message conversation.
|
|
7
|
+
*/
|
|
8
|
+
conversationId?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Identifier for a broader user or application session.
|
|
11
|
+
*/
|
|
12
|
+
sessionId?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Metadata keys emitted for Vercel AI SDK telemetry spans.
|
|
16
|
+
*/
|
|
17
|
+
export type ModunaTelemetryMetadataKey = "moduna.conversation.id" | "moduna.session.id";
|
|
18
|
+
/**
|
|
19
|
+
* Vercel AI SDK metadata generated from Moduna trace context.
|
|
20
|
+
*/
|
|
21
|
+
export type ModunaTelemetryMetadata = Partial<Record<ModunaTelemetryMetadataKey, string>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@moduna/otel",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "One-line OpenTelemetry setup for Moduna AI traces.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/src/index.js",
|
|
9
|
+
"types": "./dist/src/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/src/index.d.ts",
|
|
13
|
+
"import": "./dist/src/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@langchain/core": "^1.1.46",
|
|
26
|
+
"@opentelemetry/api": "^1.9.1",
|
|
27
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.218.0",
|
|
28
|
+
"@opentelemetry/resources": "^2.7.1",
|
|
29
|
+
"@opentelemetry/sdk-node": "^0.218.0",
|
|
30
|
+
"@opentelemetry/semantic-conventions": "^1.41.1",
|
|
31
|
+
"langchain": "^1.4.0",
|
|
32
|
+
"openai": "^6.38.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@ai-sdk/google": "^3.0.73",
|
|
36
|
+
"@langchain/google-genai": "^2.1.30",
|
|
37
|
+
"@types/node": "^25.6.2",
|
|
38
|
+
"ai": "^6.0.182",
|
|
39
|
+
"biome": "^0.3.3",
|
|
40
|
+
"tsx": "^4.12.0",
|
|
41
|
+
"typescript": "^6.0.3"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsc",
|
|
45
|
+
"format": "biome format",
|
|
46
|
+
"lint": "biome lint",
|
|
47
|
+
"typecheck": "tsc --noEmit -p tsconfig.test.json",
|
|
48
|
+
"test": "pnpm run build && pnpm run typecheck",
|
|
49
|
+
"pack:dry-run": "pnpm pack --dry-run",
|
|
50
|
+
"test:vercel-ai-sdk": "tsx test/vercel-ai-sdk.ts",
|
|
51
|
+
"test:langchain": "tsx test/langchain.ts"
|
|
52
|
+
}
|
|
53
|
+
}
|