summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-04-15 17:32:56 -0400
committerGitHub <[email protected]>2026-04-15 17:32:56 -0400
commit6bed7d469d8f6a18d5543cc668d951d0d1e09776 (patch)
treedae49a53db93eb4db06b0a0b2148d999a67c745f /packages
parent3b75f16119b914fa6eb7dd451a0f3fb20d22d69f (diff)
downloadopencode-6bed7d469d8f6a18d5543cc668d951d0d1e09776.tar.gz
opencode-6bed7d469d8f6a18d5543cc668d951d0d1e09776.zip
feat(opencode): improve telemetry tracing and request spans (#22653)
Diffstat (limited to 'packages')
-rw-r--r--packages/opencode/src/effect/app-runtime.ts3
-rw-r--r--packages/opencode/src/effect/run-service.ts2
-rw-r--r--packages/opencode/src/server/instance/config.ts34
-rw-r--r--packages/opencode/src/server/instance/session.ts28
-rw-r--r--packages/opencode/src/server/instance/trace.ts33
5 files changed, 68 insertions, 32 deletions
diff --git a/packages/opencode/src/effect/app-runtime.ts b/packages/opencode/src/effect/app-runtime.ts
index 5948bd25e..257922daf 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<typeof rt, "runSync" | "runPromise" | "runPromiseExit" | "runFork" | "runCallback" | "dispose">
diff --git a/packages/opencode/src/effect/run-service.ts b/packages/opencode/src/effect/run-service.ts
index 13104c88b..3de82e0d1 100644
--- a/packages/opencode/src/effect/run-service.ts
+++ b/packages/opencode/src/effect/run-service.ts
@@ -38,7 +38,7 @@ export function attach<A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A
export function makeRuntime<I, S, E>(service: Context.Service<I, S>, layer: Layer.Layer<I, E>) {
let rt: ManagedRuntime.ManagedRuntime<I, E> | 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: <A, Err>(fn: (svc: S) => Effect.Effect<A, Err, I>) => 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 41d5872c9..aa770726d 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 4f02e35fa..0bce3085e 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 000000000..b3adbb4c8
--- /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<typeof AppRuntime.runPromise>[0] extends Effect.Effect<any, any, infer R> ? R : never
+
+export function runRequest<A, E>(name: string, c: Context, effect: Effect.Effect<A, E, AppEnv>) {
+ 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<C extends Context, A, E>(
+ name: string,
+ c: C,
+ effect: (c: C) => Effect.gen.Return<A, E, AppEnv>,
+) {
+ return c.json(
+ await runRequest(
+ name,
+ c,
+ Effect.gen(() => effect(c)),
+ ),
+ )
+}