From 6bed7d469d8f6a18d5543cc668d951d0d1e09776 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Wed, 15 Apr 2026 17:32:56 -0400 Subject: [PATCH] feat(opencode): improve telemetry tracing and request spans (#22653) --- packages/opencode/src/effect/app-runtime.ts | 3 +- packages/opencode/src/effect/run-service.ts | 2 +- .../opencode/src/server/instance/config.ts | 34 ++++++++----------- .../opencode/src/server/instance/session.ts | 28 +++++++++------ .../opencode/src/server/instance/trace.ts | 33 ++++++++++++++++++ 5 files changed, 68 insertions(+), 32 deletions(-) create mode 100644 packages/opencode/src/server/instance/trace.ts diff --git a/packages/opencode/src/effect/app-runtime.ts b/packages/opencode/src/effect/app-runtime.ts index 5948bd25e6..257922dafe 100644 --- a/packages/opencode/src/effect/app-runtime.ts +++ b/packages/opencode/src/effect/app-runtime.ts @@ -49,7 +49,6 @@ import { ShareNext } from "@/share/share-next" import { SessionShare } from "@/share/session" export const AppLayer = Layer.mergeAll( - Observability.layer, AppFileSystem.defaultLayer, Bus.defaultLayer, Auth.defaultLayer, @@ -95,7 +94,7 @@ export const AppLayer = Layer.mergeAll( Installation.defaultLayer, ShareNext.defaultLayer, SessionShare.defaultLayer, -) +).pipe(Layer.provideMerge(Observability.layer)) const rt = ManagedRuntime.make(AppLayer, { memoMap }) type Runtime = Pick diff --git a/packages/opencode/src/effect/run-service.ts b/packages/opencode/src/effect/run-service.ts index 13104c88b3..3de82e0d11 100644 --- a/packages/opencode/src/effect/run-service.ts +++ b/packages/opencode/src/effect/run-service.ts @@ -38,7 +38,7 @@ export function attach(effect: Effect.Effect): Effect.Effect(service: Context.Service, layer: Layer.Layer) { let rt: ManagedRuntime.ManagedRuntime | undefined - const getRuntime = () => (rt ??= ManagedRuntime.make(Layer.merge(layer, Observability.layer), { memoMap })) + const getRuntime = () => (rt ??= ManagedRuntime.make(Layer.provideMerge(layer, Observability.layer), { memoMap })) return { runSync: (fn: (svc: S) => Effect.Effect) => getRuntime().runSync(attach(service.use(fn))), diff --git a/packages/opencode/src/server/instance/config.ts b/packages/opencode/src/server/instance/config.ts index 41d5872c98..aa770726df 100644 --- a/packages/opencode/src/server/instance/config.ts +++ b/packages/opencode/src/server/instance/config.ts @@ -5,12 +5,10 @@ import { Config } from "../../config/config" import { Provider } from "../../provider/provider" import { mapValues } from "remeda" import { errors } from "../error" -import { Log } from "../../util/log" import { lazy } from "../../util/lazy" import { AppRuntime } from "../../effect/app-runtime" import { Effect } from "effect" - -const log = Log.create({ service: "server" }) +import { jsonRequest } from "./trace" export const ConfigRoutes = lazy(() => new Hono() @@ -31,9 +29,11 @@ export const ConfigRoutes = lazy(() => }, }, }), - async (c) => { - return c.json(await AppRuntime.runPromise(Config.Service.use((cfg) => cfg.get()))) - }, + async (c) => + jsonRequest("ConfigRoutes.get", c, function* () { + const cfg = yield* Config.Service + return yield* cfg.get() + }), ) .patch( "/", @@ -82,18 +82,14 @@ export const ConfigRoutes = lazy(() => }, }, }), - async (c) => { - using _ = log.time("providers") - const providers = await AppRuntime.runPromise( - Effect.gen(function* () { - const svc = yield* Provider.Service - return mapValues(yield* svc.list(), (item) => item) - }), - ) - return c.json({ - providers: Object.values(providers), - default: mapValues(providers, (item) => Provider.sort(Object.values(item.models))[0].id), - }) - }, + async (c) => + jsonRequest("ConfigRoutes.providers", c, function* () { + const svc = yield* Provider.Service + const providers = mapValues(yield* svc.list(), (item) => item) + return { + providers: Object.values(providers), + default: mapValues(providers, (item) => Provider.sort(Object.values(item.models))[0].id), + } + }), ), ) diff --git a/packages/opencode/src/server/instance/session.ts b/packages/opencode/src/server/instance/session.ts index 4f02e35fac..0bce3085e0 100644 --- a/packages/opencode/src/server/instance/session.ts +++ b/packages/opencode/src/server/instance/session.ts @@ -26,6 +26,7 @@ import { errors } from "../error" import { lazy } from "../../util/lazy" import { Bus } from "../../bus" import { NamedError } from "@opencode-ai/shared/util/error" +import { jsonRequest } from "./trace" const log = Log.create({ service: "server" }) @@ -94,10 +95,11 @@ export const SessionRoutes = lazy(() => ...errors(400), }, }), - async (c) => { - const result = await AppRuntime.runPromise(SessionStatus.Service.use((svc) => svc.list())) - return c.json(Object.fromEntries(result)) - }, + async (c) => + jsonRequest("SessionRoutes.status", c, function* () { + const svc = yield* SessionStatus.Service + return Object.fromEntries(yield* svc.list()) + }), ) .get( "/:sessionID", @@ -126,8 +128,10 @@ export const SessionRoutes = lazy(() => ), async (c) => { const sessionID = c.req.valid("param").sessionID - const session = await AppRuntime.runPromise(Session.Service.use((svc) => svc.get(sessionID))) - return c.json(session) + return jsonRequest("SessionRoutes.get", c, function* () { + const session = yield* Session.Service + return yield* session.get(sessionID) + }) }, ) .get( @@ -157,8 +161,10 @@ export const SessionRoutes = lazy(() => ), async (c) => { const sessionID = c.req.valid("param").sessionID - const session = await AppRuntime.runPromise(Session.Service.use((svc) => svc.children(sessionID))) - return c.json(session) + return jsonRequest("SessionRoutes.children", c, function* () { + const session = yield* Session.Service + return yield* session.children(sessionID) + }) }, ) .get( @@ -187,8 +193,10 @@ export const SessionRoutes = lazy(() => ), async (c) => { const sessionID = c.req.valid("param").sessionID - const todos = await AppRuntime.runPromise(Todo.Service.use((svc) => svc.get(sessionID))) - return c.json(todos) + return jsonRequest("SessionRoutes.todo", c, function* () { + const todo = yield* Todo.Service + return yield* todo.get(sessionID) + }) }, ) .post( diff --git a/packages/opencode/src/server/instance/trace.ts b/packages/opencode/src/server/instance/trace.ts new file mode 100644 index 0000000000..b3adbb4c80 --- /dev/null +++ b/packages/opencode/src/server/instance/trace.ts @@ -0,0 +1,33 @@ +import type { Context } from "hono" +import { Effect } from "effect" +import { AppRuntime } from "../../effect/app-runtime" + +type AppEnv = Parameters[0] extends Effect.Effect ? R : never + +export function runRequest(name: string, c: Context, effect: Effect.Effect) { + const url = new URL(c.req.url) + return AppRuntime.runPromise( + effect.pipe( + Effect.withSpan(name, { + attributes: { + "http.method": c.req.method, + "http.path": url.pathname, + }, + }), + ), + ) +} + +export async function jsonRequest( + name: string, + c: C, + effect: (c: C) => Effect.gen.Return, +) { + return c.json( + await runRequest( + name, + c, + Effect.gen(() => effect(c)), + ), + ) +}