Forráskód Böngészése

refactor(opencode): defer legacy provider policy integration

Dax Raad 2 hónapja
szülő
commit
6ad2967db9

+ 3 - 12
packages/opencode/src/provider/provider.ts

@@ -29,9 +29,6 @@ import { ModelID, ProviderID } from "./schema"
 import { ModelStatus } from "./model-status"
 import { RuntimeFlags } from "@/effect/runtime-flags"
 import { ProviderError } from "./error"
-import { LocationServiceMap } from "@opencode-ai/core/location-layer"
-import { Policy } from "@opencode-ai/core/policy"
-import { AbsolutePath } from "@opencode-ai/core/schema"
 
 const log = Log.create({ service: "provider" })
 const OPENAI_HEADER_TIMEOUT_DEFAULT = 10_000
@@ -1210,11 +1207,9 @@ export const layer = Layer.effect(
     const plugin = yield* Plugin.Service
     const modelsDevSvc = yield* ModelsDev.Service
     const runtimeFlags = yield* RuntimeFlags.Service
-    const locations = yield* LocationServiceMap
 
-    const state = yield* InstanceState.make<State>((ctx) =>
+    const state = yield* InstanceState.make<State>(() =>
       Effect.gen(function* () {
-        const policy = yield* Policy.Service
         using _ = log.time("state")
         const bridge = yield* EffectBridge.make()
         const cfg = yield* config.get()
@@ -1485,10 +1480,7 @@ export const layer = Layer.effect(
 
         for (const [id, provider] of Object.entries(providers)) {
           const providerID = ProviderID.make(id)
-          if (
-            !isProviderAllowed(providerID) ||
-            (yield* policy.evaluate("provider.use", providerID, "allow")) === "deny"
-          ) {
+          if (!isProviderAllowed(providerID)) {
             delete providers[providerID]
             continue
           }
@@ -1545,7 +1537,7 @@ export const layer = Layer.effect(
           modelLoaders,
           varsLoaders,
         }
-      }).pipe(Effect.provide(locations.get({ directory: AbsolutePath.make(ctx.directory) }))),
+      }),
     )
 
     const list = Effect.fn("Provider.list")(() => InstanceState.use(state, (s) => s.providers))
@@ -1881,7 +1873,6 @@ export const defaultLayer = Layer.suspend(() =>
     Layer.provide(Plugin.defaultLayer),
     Layer.provide(ModelsDev.defaultLayer),
     Layer.provide(RuntimeFlags.defaultLayer),
-    Layer.provide(LocationServiceMap.layer),
   ),
 )
 

+ 55 - 64
packages/opencode/test/provider/header-timeout.test.ts

@@ -3,8 +3,9 @@ import { createServer, type Server } from "node:http"
 import { streamText } from "ai"
 import { Effect, Layer } from "effect"
 import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
-import { disposeAllInstances, provideTmpdirInstance } from "../fixture/fixture"
+import { disposeAllInstances, provideTmpdirInstance, provideTmpdirServer } from "../fixture/fixture"
 import { testEffect } from "../lib/effect"
+import { reply, TestLLMServer } from "../lib/llm-server"
 import { testProviderConfig } from "../lib/test-provider"
 import { Env } from "@/env"
 import { Plugin } from "@/plugin"
@@ -21,66 +22,70 @@ const it = testEffect(
     Provider.defaultLayer,
     Env.defaultLayer,
     Plugin.defaultLayer,
+    TestLLMServer.layer,
     CrossSpawnSpawner.defaultLayer,
   ),
 )
 
 it.live("headerTimeout does not abort delayed SSE body after headers arrive", () =>
-  Effect.gen(function* () {
-    const server = yield* Effect.acquireRelease(
-      Effect.promise(() => delayedBodyServer(250)),
-      (server) => Effect.sync(() => server.server.close()),
-    )
+  provideTmpdirServer(
+    ({ llm }) =>
+      Effect.gen(function* () {
+        yield* llm.push(reply().wait(Bun.sleep(250)).text("late").stop())
 
-    yield* provideTmpdirInstance(
-      () =>
-        Effect.gen(function* () {
-          const provider = yield* Provider.Service
-          const model = yield* provider.getModel(ProviderID.make("test"), ModelID.make("test-model"))
-          const result = streamText({
-            model: yield* provider.getLanguage(model),
-            messages: [{ role: "user", content: "hello" }],
-          })
+        const provider = yield* Provider.Service
+        const model = yield* provider.getModel(ProviderID.make("test"), ModelID.make("test-model"))
+        const result = streamText({
+          model: yield* provider.getLanguage(model),
+          messages: [{ role: "user", content: "hello" }],
+        })
 
-          expect(yield* Effect.promise(() => result.text)).toBe("late")
-        }),
-      { config: providerConfig(server.url, { headerTimeout: 50 }) },
-    )
-  }),
+        expect(yield* Effect.promise(() => result.text)).toBe("late")
+      }),
+    {
+      config: (url) => {
+        const config = testProviderConfig(url)
+        return {
+          ...config,
+          provider: {
+            test: {
+              ...config.provider.test,
+              options: { ...config.provider.test.options, headerTimeout: 50 },
+            },
+          },
+        }
+      },
+    },
+  ),
 )
 
 it.live("chunkTimeout raises a response stream error when SSE body stalls", () =>
-  Effect.gen(function* () {
-    const server = yield* Effect.acquireRelease(
-      Effect.promise(() => delayedBodyServer(250)),
-      (server) => Effect.sync(() => server.server.close()),
-    )
-
-    yield* provideTmpdirInstance(
-      () =>
-        Effect.gen(function* () {
-          const provider = yield* Provider.Service
-          const model = yield* provider.getModel(ProviderID.make("test"), ModelID.make("test-model"))
-          const result = streamText({
-            model: yield* provider.getLanguage(model),
-            onError() {},
-            messages: [{ role: "user", content: "hello" }],
-          })
-
-          const error = yield* Effect.promise(async () => {
-            try {
-              for await (const part of result.fullStream) {
-                if (part.type === "error") return part.error
-              }
-            } catch (error) {
-              return error
+  provideTmpdirServer(
+    ({ llm }) =>
+      Effect.gen(function* () {
+        yield* llm.push(reply().wait(Bun.sleep(250)).text("late").stop())
+
+        const provider = yield* Provider.Service
+        const model = yield* provider.getModel(ProviderID.make("test"), ModelID.make("test-model"))
+        const result = streamText({
+          model: yield* provider.getLanguage(model),
+          onError() {},
+          messages: [{ role: "user", content: "hello" }],
+        })
+
+        const error = yield* Effect.promise(async () => {
+          try {
+            for await (const part of result.fullStream) {
+              if (part.type === "error") return part.error
             }
-          })
-          expect(error).toBeInstanceOf(ProviderError.ResponseStreamError)
-        }),
-      { config: providerConfig(server.url, { chunkTimeout: 50 }) },
-    )
-  }),
+          } catch (error) {
+            return error
+          }
+        })
+        expect(error).toBeInstanceOf(ProviderError.ResponseStreamError)
+      }),
+    { config: (url) => providerConfig(url, { chunkTimeout: 50 }) },
+  ),
 )
 
 it.live("headerTimeout aborts when response headers do not arrive", () =>
@@ -200,20 +205,6 @@ async function delayedHeaderServer(delay: number): Promise<{ server: Server; url
   return { server, url: `http://127.0.0.1:${address.port}` }
 }
 
-async function delayedBodyServer(delay: number): Promise<{ server: Server; url: string }> {
-  const server = createServer((_, res) => {
-    res.writeHead(200, { "content-type": "text/event-stream" })
-    res.flushHeaders()
-    setTimeout(() => {
-      res.end('data: {"choices":[{"delta":{"content":"late"}}]}\n\ndata: [DONE]\n\n')
-    }, delay)
-  })
-  await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve))
-  const address = server.address()
-  if (!address || typeof address === "string") throw new Error("server did not bind to a TCP port")
-  return { server, url: `http://127.0.0.1:${address.port}` }
-}
-
 function withAuthContent<A, E, R>(self: Effect.Effect<A, E, R>, value: Record<string, unknown> = defaultAuthContent()) {
   return Effect.acquireUseRelease(
     Effect.sync(() => {