Selaa lähdekoodia

fix(opencode): export AI SDK telemetry spans (#22526)

Kit Langton 2 päivää sitten
vanhempi
sitoutus
f73ff781e7

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 219 - 166
bun.lock


+ 3 - 2
package.json

@@ -26,7 +26,8 @@
       "packages/slack"
     ],
     "catalog": {
-      "@effect/platform-node": "4.0.0-beta.46",
+      "@effect/opentelemetry": "4.0.0-beta.48",
+      "@effect/platform-node": "4.0.0-beta.48",
       "@types/bun": "1.3.11",
       "@types/cross-spawn": "6.0.6",
       "@octokit/rest": "22.0.0",
@@ -47,7 +48,7 @@
       "dompurify": "3.3.1",
       "drizzle-kit": "1.0.0-beta.19-d95b7a4",
       "drizzle-orm": "1.0.0-beta.19-d95b7a4",
-      "effect": "4.0.0-beta.46",
+      "effect": "4.0.0-beta.48",
       "ai": "6.0.158",
       "cross-spawn": "7.0.6",
       "hono": "4.10.7",

+ 4 - 0
packages/opencode/package.json

@@ -98,6 +98,7 @@
     "@ai-sdk/xai": "3.0.75",
     "@aws-sdk/credential-providers": "3.993.0",
     "@clack/prompts": "1.0.0-alpha.1",
+    "@effect/opentelemetry": "catalog:",
     "@effect/platform-node": "catalog:",
     "@gitlab/opencode-gitlab-auth": "1.3.3",
     "@hono/node-server": "1.19.11",
@@ -115,6 +116,9 @@
     "@opencode-ai/script": "workspace:*",
     "@opencode-ai/sdk": "workspace:*",
     "@opencode-ai/util": "workspace:*",
+    "@opentelemetry/exporter-trace-otlp-http": "0.214.0",
+    "@opentelemetry/sdk-trace-base": "2.6.1",
+    "@opentelemetry/sdk-trace-node": "2.6.1",
     "@openrouter/ai-sdk-provider": "2.5.1",
     "@opentui/core": "0.1.99",
     "@opentui/solid": "0.1.99",

+ 6 - 0
packages/opencode/src/agent/agent.ts

@@ -21,6 +21,8 @@ import { Plugin } from "@/plugin"
 import { Skill } from "../skill"
 import { Effect, Context, Layer } from "effect"
 import { InstanceState } from "@/effect/instance-state"
+import * as Option from "effect/Option"
+import * as OtelTracer from "@effect/opentelemetry/Tracer"
 
 export namespace Agent {
   export const Info = z
@@ -334,6 +336,9 @@ export namespace Agent {
           const model = input.model ?? (yield* provider.defaultModel())
           const resolved = yield* provider.getModel(model.providerID, model.modelID)
           const language = yield* provider.getLanguage(resolved)
+          const tracer = cfg.experimental?.openTelemetry
+            ? Option.getOrUndefined(yield* Effect.serviceOption(OtelTracer.OtelTracer))
+            : undefined
 
           const system = [PROMPT_GENERATE]
           yield* plugin.trigger("experimental.chat.system.transform", { model: resolved }, { system })
@@ -346,6 +351,7 @@ export namespace Agent {
           const params = {
             experimental_telemetry: {
               isEnabled: cfg.experimental?.openTelemetry,
+              tracer,
               metadata: {
                 userId: cfg.username ?? "unknown",
               },

+ 1 - 1
packages/opencode/src/effect/app-runtime.ts

@@ -1,6 +1,6 @@
 import { Layer, ManagedRuntime } from "effect"
 import { attach, memoMap } from "./run-service"
-import { Observability } from "./oltp"
+import { Observability } from "./observability"
 
 import { AppFileSystem } from "@/filesystem"
 import { Bus } from "@/bus"

+ 1 - 1
packages/opencode/src/effect/bootstrap-runtime.ts

@@ -10,7 +10,7 @@ import { File } from "@/file"
 import { Vcs } from "@/project/vcs"
 import { Snapshot } from "@/snapshot"
 import { Bus } from "@/bus"
-import { Observability } from "./oltp"
+import { Observability } from "./observability"
 
 export const BootstrapLayer = Layer.mergeAll(
   Plugin.defaultLayer,

+ 68 - 0
packages/opencode/src/effect/observability.ts

@@ -0,0 +1,68 @@
+import { Effect, Layer, Logger } from "effect"
+import { FetchHttpClient } from "effect/unstable/http"
+import { OtlpLogger, OtlpSerialization } from "effect/unstable/observability"
+import { EffectLogger } from "@/effect/logger"
+import { Flag } from "@/flag/flag"
+import { CHANNEL, VERSION } from "@/installation/meta"
+
+export namespace Observability {
+  const base = Flag.OTEL_EXPORTER_OTLP_ENDPOINT
+  export const enabled = !!base
+
+  const headers = Flag.OTEL_EXPORTER_OTLP_HEADERS
+    ? Flag.OTEL_EXPORTER_OTLP_HEADERS.split(",").reduce(
+        (acc, x) => {
+          const [key, value] = x.split("=")
+          acc[key] = value
+          return acc
+        },
+        {} as Record<string, string>,
+      )
+    : undefined
+
+  const resource = {
+    serviceName: "opencode",
+    serviceVersion: VERSION,
+    attributes: {
+      "deployment.environment.name": CHANNEL === "local" ? "local" : CHANNEL,
+      "opencode.client": Flag.OPENCODE_CLIENT,
+    },
+  }
+
+  const logs = Logger.layer(
+    [
+      EffectLogger.logger,
+      OtlpLogger.make({
+        url: `${base}/v1/logs`,
+        resource,
+        headers,
+      }),
+    ],
+    { mergeWithExisting: false },
+  ).pipe(Layer.provide(OtlpSerialization.layerJson), Layer.provide(FetchHttpClient.layer))
+
+  const traces = async () => {
+    const NodeSdk = await import("@effect/opentelemetry/NodeSdk")
+    const OTLP = await import("@opentelemetry/exporter-trace-otlp-http")
+    const SdkBase = await import("@opentelemetry/sdk-trace-base")
+
+    return NodeSdk.layer(() => ({
+      resource,
+      spanProcessor: new SdkBase.BatchSpanProcessor(
+        new OTLP.OTLPTraceExporter({
+          url: `${base}/v1/traces`,
+          headers,
+        }),
+      ),
+    }))
+  }
+
+  export const layer = !base
+    ? EffectLogger.layer
+    : Layer.unwrap(
+        Effect.gen(function* () {
+          const trace = yield* Effect.promise(traces)
+          return Layer.mergeAll(trace, logs)
+        }),
+      )
+}

+ 0 - 41
packages/opencode/src/effect/oltp.ts

@@ -1,41 +0,0 @@
-import { Duration, Layer } from "effect"
-import { FetchHttpClient } from "effect/unstable/http"
-import { Otlp } from "effect/unstable/observability"
-import { EffectLogger } from "@/effect/logger"
-import { Flag } from "@/flag/flag"
-import { CHANNEL, VERSION } from "@/installation/meta"
-
-export namespace Observability {
-  const base = Flag.OTEL_EXPORTER_OTLP_ENDPOINT
-  export const enabled = !!base
-
-  const resource = {
-    serviceName: "opencode",
-    serviceVersion: VERSION,
-    attributes: {
-      "deployment.environment.name": CHANNEL === "local" ? "local" : CHANNEL,
-      "opencode.client": Flag.OPENCODE_CLIENT,
-    },
-  }
-
-  const headers = Flag.OTEL_EXPORTER_OTLP_HEADERS
-    ? Flag.OTEL_EXPORTER_OTLP_HEADERS.split(",").reduce(
-        (acc, x) => {
-          const [key, value] = x.split("=")
-          acc[key] = value
-          return acc
-        },
-        {} as Record<string, string>,
-      )
-    : undefined
-
-  export const layer = !base
-    ? EffectLogger.layer
-    : Otlp.layerJson({
-        baseUrl: base,
-        loggerExportInterval: Duration.seconds(1),
-        loggerMergeWithExisting: true,
-        resource,
-        headers,
-      }).pipe(Layer.provide(EffectLogger.layer), Layer.provide(FetchHttpClient.layer))
-}

+ 1 - 1
packages/opencode/src/effect/run-service.ts

@@ -3,7 +3,7 @@ import * as Context from "effect/Context"
 import { Instance } from "@/project/instance"
 import { LocalContext } from "@/util/local-context"
 import { InstanceRef, WorkspaceRef } from "./instance-ref"
-import { Observability } from "./oltp"
+import { Observability } from "./observability"
 import { WorkspaceContext } from "@/control-plane/workspace-context"
 
 export const memoMap = Layer.makeMemoMapUnsafe()

+ 8 - 0
packages/opencode/src/session/llm.ts

@@ -21,6 +21,8 @@ import { SessionID } from "@/session/schema"
 import { Auth } from "@/auth"
 import { Installation } from "@/installation"
 import { makeRuntime } from "@/effect/run-service"
+import * as Option from "effect/Option"
+import * as OtelTracer from "@effect/opentelemetry/Tracer"
 
 export namespace LLM {
   const log = Log.create({ service: "llm" })
@@ -312,6 +314,10 @@ export namespace LLM {
             })
           }
 
+          const tracer = cfg.experimental?.openTelemetry
+            ? Option.getOrUndefined(yield* Effect.serviceOption(OtelTracer.OtelTracer))
+            : undefined
+
           return streamText({
             onError(error) {
               l.error("stream error", {
@@ -383,6 +389,8 @@ export namespace LLM {
             }),
             experimental_telemetry: {
               isEnabled: cfg.experimental?.openTelemetry,
+              functionId: "session.llm",
+              tracer,
               metadata: {
                 userId: cfg.username ?? "unknown",
                 sessionId: input.sessionID,

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä