fix(core): preserve model capability semantics
This commit is contained in:
parent
77fce8b24c
commit
44a898f3ee
7 changed files with 27 additions and 59 deletions
|
|
@ -766,15 +766,15 @@ describe("Config", () => {
|
|||
settings: { apiKey: "secret" },
|
||||
models: {
|
||||
model: {
|
||||
capabilities: { tools: false, input: ["text", "image"], output: ["text"] },
|
||||
capabilities: { tools: true, input: ["text", "image"], output: ["text"] },
|
||||
settings: { reasoningEffort: "high" },
|
||||
variants: [{ id: "fast", settings: { temperature: 0.2 } }],
|
||||
},
|
||||
text: {
|
||||
capabilities: { tools: false, input: ["text"], output: ["text"] },
|
||||
capabilities: { tools: true, input: ["text"], output: ["text"] },
|
||||
},
|
||||
audio: {
|
||||
capabilities: { tools: false, input: ["audio"], output: ["audio"] },
|
||||
capabilities: { tools: true, input: ["audio"], output: ["audio"] },
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ describe("ConfigProviderPlugin.Plugin", () => {
|
|||
expect(model.modelID).toBe(ModelV2.ID.make("api-chat"))
|
||||
expect(model.name).toBe("Last")
|
||||
expect(model.capabilities).toEqual({ tools: true, input: ["text"], output: ["text"] })
|
||||
expect(defaultModel.capabilities).toEqual({ tools: false, input: ["text", "image"], output: ["text"] })
|
||||
expect(defaultModel.capabilities).toEqual({ tools: true, input: ["text", "image"], output: ["text"] })
|
||||
expect(model.enabled).toBe(false)
|
||||
expect(model.limit).toEqual({ context: 100, output: 75 })
|
||||
expect(model.cost).toEqual([
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import { EventTable } from "@opencode-ai/core/event/sql"
|
|||
import { Job } from "@opencode-ai/core/job"
|
||||
import { PermissionV2 } from "@opencode-ai/core/permission"
|
||||
import { AgentV2 } from "@opencode-ai/core/agent"
|
||||
import { Catalog } from "@opencode-ai/core/catalog"
|
||||
import { Config } from "@opencode-ai/core/config"
|
||||
import { Project } from "@opencode-ai/core/project"
|
||||
import { ProjectTable } from "@opencode-ai/core/project/sql"
|
||||
|
|
@ -38,7 +37,6 @@ import { Instructions } from "@opencode-ai/core/instructions"
|
|||
import { SkillGuidance } from "@opencode-ai/core/skill/guidance"
|
||||
import { ReferenceGuidance } from "@opencode-ai/core/reference/guidance"
|
||||
import { McpGuidance } from "@opencode-ai/core/mcp/guidance"
|
||||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { describe, expect } from "bun:test"
|
||||
import { eq } from "drizzle-orm"
|
||||
import { Effect, Layer } from "effect"
|
||||
|
|
@ -75,20 +73,6 @@ const model = OpenAIChat.route
|
|||
})
|
||||
.model({ id: "gpt-4o-mini" })
|
||||
const models = SessionRunnerModel.layerWith(() => Effect.succeed(SessionRunnerModel.resolved(model)))
|
||||
const catalog = Layer.mock(Catalog.Service, {
|
||||
provider: {
|
||||
get: () => Effect.die("unused"),
|
||||
all: () => Effect.die("unused"),
|
||||
available: () => Effect.die("unused"),
|
||||
},
|
||||
model: {
|
||||
get: (providerID, modelID) => Effect.succeed(ModelV2.Info.empty(providerID, modelID)),
|
||||
all: () => Effect.die("unused"),
|
||||
available: () => Effect.die("unused"),
|
||||
default: () => Effect.die("unused"),
|
||||
small: () => Effect.die("unused"),
|
||||
},
|
||||
})
|
||||
const systemContext = Layer.mock(InstructionBuiltIns.Service, { load: () => Effect.succeed(Instructions.empty) })
|
||||
const instructionContext = Layer.mock(InstructionDiscovery.Service, { load: () => Effect.succeed(Instructions.empty) })
|
||||
const skillGuidance = Layer.mock(SkillGuidance.Service, { load: () => Effect.succeed(Instructions.empty) })
|
||||
|
|
@ -99,7 +83,6 @@ const runnerLayer = AppNodeBuilder.build(SessionRunnerLLM.node, [
|
|||
[Snapshot.node, Snapshot.noopLayer],
|
||||
[LayerNodePlatform.llmClient, client],
|
||||
[SessionRunnerModel.node, models],
|
||||
[Catalog.node, catalog],
|
||||
[InstructionBuiltIns.node, systemContext],
|
||||
[InstructionDiscovery.node, instructionContext],
|
||||
[Location.node, Location.boundNode({ directory: AbsolutePath.make("/project") })],
|
||||
|
|
@ -150,7 +133,6 @@ const it = testEffect(
|
|||
[PermissionV2.node, permission],
|
||||
[ToolOutputStore.node, ToolOutputStore.nodeWithoutConfig],
|
||||
[SessionRunnerModel.node, models],
|
||||
[Catalog.node, catalog],
|
||||
[InstructionBuiltIns.node, systemContext],
|
||||
[InstructionDiscovery.node, instructionContext],
|
||||
[Location.node, Location.boundNode({ directory: AbsolutePath.make("/project") })],
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ import { ToolRegistry } from "@opencode-ai/core/tool/registry"
|
|||
import { QuestionTool } from "@opencode-ai/core/tool/question"
|
||||
import { ToolOutputStore } from "@opencode-ai/core/tool-output-store"
|
||||
import { AgentV2 } from "@opencode-ai/core/agent"
|
||||
import { Catalog } from "@opencode-ai/core/catalog"
|
||||
import { Config } from "@opencode-ai/core/config"
|
||||
import { ConfigCompaction } from "@opencode-ai/core/config/compaction"
|
||||
import { Tool } from "@opencode-ai/core/tool/tool"
|
||||
|
|
@ -236,34 +235,19 @@ const echo = Layer.effectDiscard(
|
|||
const echoNode = makeLocationNode({ name: "test/session-runner-tools", layer: echo, deps: [ToolRegistry.node] })
|
||||
let modelResolveHook = Effect.void
|
||||
let currentModel = model
|
||||
let modelCapabilities = ModelV2.Capabilities.defaults()
|
||||
const models = SessionRunnerModel.layerWith((session) =>
|
||||
modelResolveHook.pipe(
|
||||
Effect.as(
|
||||
SessionRunnerModel.resolved(
|
||||
session.model?.id === "replacement" ? replacementModel : currentModel,
|
||||
session.model?.variant,
|
||||
[],
|
||||
modelCapabilities,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
let catalogCapabilities = ModelV2.Capabilities.defaults({ tools: true })
|
||||
const catalog = Layer.mock(Catalog.Service, {
|
||||
provider: {
|
||||
get: () => Effect.die("unused"),
|
||||
all: () => Effect.die("unused"),
|
||||
available: () => Effect.die("unused"),
|
||||
},
|
||||
model: {
|
||||
get: (providerID, modelID) =>
|
||||
Effect.succeed(
|
||||
ModelV2.Info.make({ ...ModelV2.Info.empty(providerID, modelID), capabilities: catalogCapabilities }),
|
||||
),
|
||||
all: () => Effect.die("unused"),
|
||||
available: () => Effect.die("unused"),
|
||||
default: () => Effect.die("unused"),
|
||||
small: () => Effect.die("unused"),
|
||||
},
|
||||
})
|
||||
const systemContextKey = Instructions.Key.make("test/context")
|
||||
let systemBaseline = "Initial context"
|
||||
let systemRemoved = false
|
||||
|
|
@ -330,7 +314,6 @@ const runnerLayer = AppNodeBuilder.build(SessionRunnerLLM.node, [
|
|||
[Snapshot.node, Snapshot.noopLayer],
|
||||
[LayerNodePlatform.llmClient, client],
|
||||
[SessionRunnerModel.node, models],
|
||||
[Catalog.node, catalog],
|
||||
[InstructionBuiltIns.node, systemContext],
|
||||
[InstructionDiscovery.node, instructionContext],
|
||||
[Location.node, Location.boundNode({ directory: AbsolutePath.make("/project") })],
|
||||
|
|
@ -385,7 +368,6 @@ const it = testEffect(
|
|||
[LayerNodePlatform.llmClient, client],
|
||||
[PermissionV2.node, permission],
|
||||
[SessionRunnerModel.node, models],
|
||||
[Catalog.node, catalog],
|
||||
[InstructionBuiltIns.node, systemContext],
|
||||
[InstructionDiscovery.node, instructionContext],
|
||||
[Location.node, Location.boundNode({ directory: AbsolutePath.make("/project") })],
|
||||
|
|
@ -433,7 +415,7 @@ const setup = Effect.gen(function* () {
|
|||
systemLoadHook = Effect.void
|
||||
modelResolveHook = Effect.void
|
||||
currentModel = model
|
||||
catalogCapabilities = ModelV2.Capabilities.defaults({ tools: true })
|
||||
modelCapabilities = ModelV2.Capabilities.defaults()
|
||||
skillBaselines.clear()
|
||||
responses = undefined
|
||||
streamFailure = undefined
|
||||
|
|
@ -812,7 +794,7 @@ describe("SessionRunnerLLM", () => {
|
|||
it.effect("does not advertise tools to a model without tool capability", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* setup
|
||||
catalogCapabilities = ModelV2.Capabilities.defaults()
|
||||
modelCapabilities = ModelV2.Capabilities.defaults({ tools: false })
|
||||
const session = yield* SessionV2.Service
|
||||
yield* session.prompt({ sessionID, prompt: PromptInput.Prompt.make({ text: "No tools" }), resume: false })
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue