langsmith 0.6.0 → 0.6.1

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.
@@ -1,385 +0,0 @@
1
- // import type { Event, FilePart, Message, Model, Part } from "@opencode-ai/sdk";
2
- import { RunTree } from "../../run_trees.js";
3
- import { Client } from "../../index.js";
4
- const dedupeParts = (parts) => {
5
- const partById = {};
6
- for (const part of parts) {
7
- partById[part.id] = { ...partById[part.id], ...part };
8
- }
9
- return Object.values(partById);
10
- };
11
- const convertToStandardContentBlock = (part) => {
12
- // Ignore AI SDK specific parts
13
- if (part.type === "step-start" || part.type === "step-finish") {
14
- return [];
15
- }
16
- if (part.type === "text") {
17
- return {
18
- type: "text",
19
- text: part.text,
20
- extras: part.metadata,
21
- };
22
- }
23
- if (part.type === "reasoning") {
24
- return {
25
- type: "thinking",
26
- thinking: part.text,
27
- };
28
- }
29
- if (part.type === "file") {
30
- return {
31
- type: "file",
32
- id: part.filename ?? part.id,
33
- url: part.url,
34
- mime_type: part.mime,
35
- };
36
- }
37
- if (part.type === "tool") {
38
- return {
39
- type: "tool_use",
40
- name: part.tool,
41
- input: part.state.input,
42
- id: part.callID,
43
- };
44
- }
45
- if (part.type === "compaction") {
46
- return {
47
- type: "compaction",
48
- data: { auto: part.auto },
49
- };
50
- }
51
- return {
52
- type: "non_standard",
53
- value: part,
54
- };
55
- };
56
- const convertToStandardMessages = (messages) => {
57
- return messages.flatMap((message) => {
58
- const parts = dedupeParts(message.parts);
59
- if (message.info?.role === "assistant") {
60
- // split out into "model message"
61
- return [
62
- {
63
- role: "assistant",
64
- content: parts.flatMap(convertToStandardContentBlock),
65
- },
66
- ...parts.flatMap((part) => {
67
- if (part.type !== "tool")
68
- return [];
69
- if (part.state.status === "completed") {
70
- return {
71
- role: "tool",
72
- content: part.state.output,
73
- name: part.tool,
74
- id: part.id,
75
- tool_call_id: part.callID,
76
- };
77
- }
78
- if (part.state.status === "error") {
79
- return {
80
- role: "tool",
81
- content: part.state.error,
82
- name: part.tool,
83
- id: part.id,
84
- tool_call_id: part.callID,
85
- };
86
- }
87
- return [];
88
- }),
89
- ];
90
- }
91
- if (message.info?.role === "user") {
92
- return {
93
- role: "user",
94
- content: parts.flatMap(convertToStandardContentBlock),
95
- };
96
- }
97
- return [];
98
- });
99
- };
100
- export class OpenCodeSessionTracer {
101
- constructor(inputConfig) {
102
- Object.defineProperty(this, "sessions", {
103
- enumerable: true,
104
- configurable: true,
105
- writable: true,
106
- value: {}
107
- });
108
- Object.defineProperty(this, "client", {
109
- enumerable: true,
110
- configurable: true,
111
- writable: true,
112
- value: void 0
113
- });
114
- Object.defineProperty(this, "inputConfig", {
115
- enumerable: true,
116
- configurable: true,
117
- writable: true,
118
- value: void 0
119
- });
120
- this.inputConfig = inputConfig ?? {};
121
- this.client = inputConfig?.client ?? new Client();
122
- }
123
- getSession(sessionID) {
124
- this.sessions[sessionID] ??= {
125
- messages: {},
126
- traces: {},
127
- history: undefined,
128
- pendingSystem: undefined,
129
- postRunQueue: [],
130
- parentID: undefined,
131
- };
132
- return this.sessions[sessionID];
133
- }
134
- getMessage(sessionID, messageID) {
135
- const session = this.getSession(sessionID);
136
- session.messages[messageID] ??= {
137
- info: undefined,
138
- parts: [],
139
- complete: false,
140
- system: undefined,
141
- };
142
- // Attach pending system to assistant messages
143
- if (session.pendingSystem != null &&
144
- session.messages[messageID]?.info?.role === "assistant") {
145
- session.messages[messageID].system = session.pendingSystem;
146
- session.pendingSystem = undefined;
147
- }
148
- return session.messages[messageID];
149
- }
150
- getProviderMetadata(run) {
151
- const info = run.info;
152
- if (!info || info.role !== "assistant")
153
- return {};
154
- const model = run.system?.model;
155
- const modelId = model?.id ?? info.modelID;
156
- const providerId = model?.providerID ?? info.providerID;
157
- const ls_invocation_params = {
158
- model: modelId,
159
- providerID: providerId,
160
- };
161
- if (model?.name)
162
- ls_invocation_params.model_display_name = model.name;
163
- if (model?.api?.id)
164
- ls_invocation_params.api_model_id = model.api.id;
165
- if (model?.api?.url)
166
- ls_invocation_params.api_url = model.api.url;
167
- if (model?.api?.npm)
168
- ls_invocation_params.api_npm_package = model.api.npm;
169
- const stepFinish = run.parts.find((part) => part.type === "step-finish");
170
- return {
171
- ls_model_name: modelId,
172
- ls_provider: providerId,
173
- ls_model_type: "chat",
174
- ls_invocation_params,
175
- usage_metadata: stepFinish
176
- ? {
177
- input_tokens: stepFinish.tokens.input,
178
- output_tokens: stepFinish.tokens.output + stepFinish.tokens.reasoning,
179
- total_tokens: stepFinish.tokens.input +
180
- stepFinish.tokens.output +
181
- stepFinish.tokens.reasoning,
182
- input_token_details: {
183
- cache_read: stepFinish.tokens.cache.read,
184
- cache_creation: stepFinish.tokens.cache.write,
185
- },
186
- }
187
- : undefined,
188
- };
189
- }
190
- async sendTrace(sessionID, runs, options) {
191
- const session = this.getSession(sessionID);
192
- const userRunIdx = runs.findIndex(({ info }) => info?.role === "user");
193
- const userRun = runs.at(userRunIdx);
194
- const agentRuns = runs.slice(userRunIdx + 1);
195
- if (userRunIdx === -1 || userRun == null)
196
- return;
197
- const parentStartTime = userRun?.info?.time?.created ?? Date.now();
198
- const parentEndTime = agentRuns
199
- .flatMap((run) => run.parts)
200
- .reduce((acc, part) => {
201
- if (!("time" in part) || part.time == null)
202
- return acc;
203
- if (!("end" in part.time) || typeof part.time.end !== "number")
204
- return acc;
205
- return Math.max(acc, part.time.end);
206
- }, parentStartTime);
207
- if (userRun?.info) {
208
- session.history ??= [];
209
- session.history.push({ info: userRun.info, parts: userRun.parts });
210
- }
211
- const parentConfig = {
212
- name: "opencode.session",
213
- run_type: "chain",
214
- start_time: parentStartTime,
215
- end_time: parentEndTime,
216
- extra: {
217
- metadata: {
218
- ls_integration: "opencode-js",
219
- ls_agent_type: "root",
220
- thread_id: sessionID,
221
- },
222
- },
223
- inputs: { messages: convertToStandardMessages([userRun]) },
224
- outputs: { messages: convertToStandardMessages(agentRuns) },
225
- ...this.inputConfig,
226
- client: this.client,
227
- };
228
- const parent = options?.parentRunTree?.createChild(parentConfig) ??
229
- new RunTree(parentConfig);
230
- session.postRunQueue.push(parent.postRun());
231
- for (const run of agentRuns) {
232
- const startTime = run.info?.time?.created ?? Date.now();
233
- const endTime = run.parts.reduce((acc, part) => {
234
- if (!("time" in part) || part.time == null)
235
- return acc;
236
- if (!("end" in part.time) || typeof part.time.end !== "number")
237
- return acc;
238
- return Math.max(acc, part.time.end);
239
- }, startTime);
240
- const parts = dedupeParts(run.parts);
241
- // Create child runs for tool parts
242
- const child = parent.createChild({
243
- name: "opencode.assistant.turn",
244
- run_type: "llm",
245
- start_time: startTime,
246
- end_time: endTime,
247
- inputs: {
248
- messages: [
249
- ...(run.system?.system
250
- ? [{ role: "system", content: run.system.system.join("\n") }]
251
- : []),
252
- ...convertToStandardMessages(session.history ?? []),
253
- ],
254
- },
255
- outputs: { messages: convertToStandardMessages([run]) },
256
- extra: { metadata: this.getProviderMetadata(run) },
257
- });
258
- session.postRunQueue.push(child.postRun());
259
- for (const toolPart of parts) {
260
- if (toolPart.type !== "tool")
261
- continue;
262
- const state = toolPart.state;
263
- // Try looking for subgraph
264
- let toolHandled = false;
265
- if (state.metadata?.sessionId) {
266
- const session = this.getSession(state.metadata.sessionId);
267
- for (const trace of Object.values(session.traces)) {
268
- if (trace.state !== "subgraph")
269
- continue;
270
- await this.sendTrace(state.metadata.sessionId, trace.runs, {
271
- parentRunTree: child,
272
- });
273
- toolHandled = true;
274
- }
275
- }
276
- if (toolHandled)
277
- continue;
278
- const tool = child.createChild({
279
- name: toolPart.tool,
280
- run_type: "tool",
281
- inputs: state.input ?? {},
282
- outputs: {
283
- output: state.output,
284
- attachments: state.attachments?.map(convertToStandardContentBlock) ??
285
- undefined,
286
- },
287
- start_time: state.time?.start ?? startTime,
288
- end_time: state.time?.end ?? endTime,
289
- error: state.error,
290
- extra: { metadata: state.metadata },
291
- });
292
- session.postRunQueue.push(tool.postRun());
293
- }
294
- if (run.info) {
295
- session.history ??= [];
296
- session.history.push({ info: run.info, parts });
297
- }
298
- }
299
- }
300
- async flush() {
301
- await Promise.all(Object.values(this.sessions).flatMap((session) => session.postRunQueue));
302
- await this.client.flush();
303
- await this.client.awaitPendingTraceBatches();
304
- }
305
- async handleSystem(input, output) {
306
- if (!input.sessionID)
307
- return;
308
- const session = this.getSession(input.sessionID);
309
- session.pendingSystem = { model: input.model, system: output.system };
310
- }
311
- async handleSessionLoad(sessionID, history) {
312
- const session = this.getSession(sessionID);
313
- if (session.history)
314
- return;
315
- session.history = await history(sessionID);
316
- }
317
- async handleEvent({ event: { properties, type } }) {
318
- if (type === "server.instance.disposed") {
319
- await this.flush();
320
- return;
321
- }
322
- const sessionID = "sessionID" in properties && typeof properties.sessionID === "string"
323
- ? properties.sessionID
324
- : undefined;
325
- if (!sessionID)
326
- return;
327
- const session = this.getSession(sessionID);
328
- let updatedID;
329
- if (type === "session.created" || type === "session.updated") {
330
- session.parentID = properties.info.parentID;
331
- }
332
- if (type === "message.updated") {
333
- const message = this.getMessage(sessionID, properties.info.id);
334
- message.info = properties.info;
335
- updatedID = properties.info.id;
336
- }
337
- if (type === "message.part.updated") {
338
- const message = this.getMessage(sessionID, properties.part.messageID);
339
- message.parts.push(properties.part);
340
- updatedID = properties.part.messageID;
341
- }
342
- if (type === "message.part.removed") {
343
- const message = this.getMessage(sessionID, properties.messageID);
344
- message.parts = message.parts.filter((part) => part.id !== properties.partID);
345
- updatedID = properties.messageID;
346
- }
347
- if (type === "message.removed") {
348
- const session = this.getSession(sessionID);
349
- delete session.messages[properties.messageID];
350
- }
351
- // Message consolidation logic
352
- const message = updatedID ? session.messages[updatedID] : undefined;
353
- if (message?.info?.role == null)
354
- return;
355
- // Skip if message is already marked as complete
356
- if (message.complete)
357
- return;
358
- message.complete =
359
- (message.info?.role === "user" && message.parts.length > 0) ||
360
- (message.info?.role === "assistant" &&
361
- message.parts.some((part) => part.type === "step-finish"));
362
- // Now we're complete, add to a trace
363
- if (message.complete) {
364
- const traceId = message.info.role === "user" ? message.info.id : message.info.parentID;
365
- session.traces[traceId] ??= { runs: [], state: false };
366
- const trace = session.traces[traceId];
367
- trace.runs.push(message);
368
- // Skip if trace is already marked as complete
369
- if (trace.state !== false)
370
- return;
371
- trace.state = trace.runs.some((run) => run.parts.some(
372
- // trace is marked complete when there's a step-finish part with reason "stop"
373
- (part) => part.type === "step-finish" && part.reason === "stop"));
374
- if (trace.state) {
375
- // If trace is part of a subagent call, mark it as a subgraph and submit
376
- // when parent is being submitted (to preserve correct dotted order)
377
- if (session.parentID) {
378
- trace.state = "subgraph";
379
- return;
380
- }
381
- await this.sendTrace(sessionID, trace.runs);
382
- }
383
- }
384
- }
385
- }
@@ -1 +0,0 @@
1
- module.exports = require('../dist/experimental/opencode/index.cjs');
@@ -1 +0,0 @@
1
- export * from '../dist/experimental/opencode/index.js'
@@ -1 +0,0 @@
1
- export * from '../dist/experimental/opencode/index.js'
@@ -1 +0,0 @@
1
- export * from '../dist/experimental/opencode/index.js'