add default script
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type { LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3FinishReason } from "@ai-sdk/provider"
|
||||
import type { LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3FinishReason, LanguageModelV3StreamPart } from "@ai-sdk/provider"
|
||||
import { simulateReadableStream } from "ai"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import { ModelID, ProviderID } from "@/provider/schema"
|
||||
@@ -39,6 +40,15 @@ const provider: Provider.Info = {
|
||||
models: { [modelID]: model },
|
||||
}
|
||||
|
||||
const defaultScript: LLMScript = {
|
||||
steps: [[{ type: "text", content: "Simulation mock response." }]],
|
||||
finish: "stop",
|
||||
}
|
||||
|
||||
function nextScript(simulation: Simulation.Interface) {
|
||||
return Effect.runPromise(simulation.nextLLM().pipe(Effect.catch(() => Effect.succeed(defaultScript))))
|
||||
}
|
||||
|
||||
function text(script: LLMScript) {
|
||||
return script.steps[0]?.flatMap((item) => (item.type === "text" || item.type === "thinking" ? [item.content] : []))
|
||||
.join("") ?? ""
|
||||
@@ -49,31 +59,33 @@ function error(script: LLMScript) {
|
||||
}
|
||||
|
||||
function stream(script: LLMScript) {
|
||||
return new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue({ type: "stream-start", warnings: [] })
|
||||
let index = 0
|
||||
for (const item of script.steps[0] ?? []) {
|
||||
index++
|
||||
if (item.type === "error") {
|
||||
controller.enqueue({ type: "error", error: new Error(item.message) })
|
||||
controller.close()
|
||||
return
|
||||
}
|
||||
const id = `simulation-${item.type}-${index}`
|
||||
if (item.type === "thinking") {
|
||||
controller.enqueue({ type: "reasoning-start", id })
|
||||
controller.enqueue({ type: "reasoning-delta", id, delta: item.content })
|
||||
controller.enqueue({ type: "reasoning-end", id })
|
||||
continue
|
||||
}
|
||||
controller.enqueue({ type: "text-start", id })
|
||||
controller.enqueue({ type: "text-delta", id, delta: item.content })
|
||||
controller.enqueue({ type: "text-end", id })
|
||||
}
|
||||
controller.enqueue({ type: "finish", finishReason: finishReason(script), usage: usage(script) })
|
||||
controller.close()
|
||||
},
|
||||
const chunks: LanguageModelV3StreamPart[] = [{ type: "stream-start", warnings: [] }]
|
||||
for (const [index, item] of (script.steps[0] ?? []).entries()) {
|
||||
if (item.type === "error") {
|
||||
chunks.push({ type: "error", error: new Error(item.message) })
|
||||
continue
|
||||
}
|
||||
const id = `simulation-${item.type}-${index + 1}`
|
||||
if (item.type === "thinking") {
|
||||
chunks.push(
|
||||
{ type: "reasoning-start", id },
|
||||
{ type: "reasoning-delta", id, delta: item.content },
|
||||
{ type: "reasoning-end", id },
|
||||
)
|
||||
continue
|
||||
}
|
||||
chunks.push(
|
||||
{ type: "text-start", id },
|
||||
{ type: "text-delta", id, delta: item.content },
|
||||
{ type: "text-end", id },
|
||||
)
|
||||
}
|
||||
chunks.push({ type: "finish", finishReason: finishReason(script), usage: usage(script) })
|
||||
|
||||
return simulateReadableStream({
|
||||
chunks,
|
||||
initialDelayInMs: 0,
|
||||
chunkDelayInMs: 0,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -105,7 +117,7 @@ function language(simulation: Simulation.Interface): LanguageModelV3 {
|
||||
modelId: modelID,
|
||||
supportedUrls: {},
|
||||
async doGenerate(_options: LanguageModelV3CallOptions) {
|
||||
const script = await Effect.runPromise(simulation.nextLLM())
|
||||
const script = await nextScript(simulation)
|
||||
const err = error(script)
|
||||
if (err?.type === "error") throw new Error(err.message)
|
||||
return {
|
||||
@@ -116,7 +128,7 @@ function language(simulation: Simulation.Interface): LanguageModelV3 {
|
||||
}
|
||||
},
|
||||
async doStream(_options: LanguageModelV3CallOptions) {
|
||||
const script = await Effect.runPromise(simulation.nextLLM())
|
||||
const script = await nextScript(simulation)
|
||||
return { stream: stream(script) }
|
||||
},
|
||||
}
|
||||
|
||||
@@ -108,11 +108,17 @@ export const layer = Layer.effect(
|
||||
Effect.gen(function* () {
|
||||
const fs = yield* AppFileSystem.Service
|
||||
const network = yield* SimulationNetwork.Service
|
||||
const state = yield* Ref.make<State>({ files: [], networkRegistrations: [], llmScripts: [], consumedLLMScripts: 0 })
|
||||
const empty: State = {
|
||||
files: [],
|
||||
networkRegistrations: [],
|
||||
llmScripts: [],
|
||||
consumedLLMScripts: 0,
|
||||
}
|
||||
const state = yield* Ref.make<State>(empty)
|
||||
|
||||
const reset = Effect.fn("Simulation.reset")(function* () {
|
||||
yield* network.reset()
|
||||
yield* Ref.set(state, { files: [], networkRegistrations: [], llmScripts: [], consumedLLMScripts: 0 })
|
||||
yield* Ref.set(state, empty)
|
||||
})
|
||||
|
||||
const seedFilesystem = Effect.fn("Simulation.seedFilesystem")(function* (input: typeof FilesystemSeedInput.Type) {
|
||||
|
||||
@@ -98,6 +98,36 @@ describe("Simulation", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("simulation provider returns a default response when no script is queued", () =>
|
||||
Effect.gen(function* () {
|
||||
const provider = yield* Provider.Service
|
||||
const model = yield* provider.defaultModel().pipe(Effect.flatMap((item) => provider.getModel(item.providerID, item.modelID)))
|
||||
const language = yield* provider.getLanguage(model)
|
||||
|
||||
const result = yield* Effect.promise(() => language.doGenerate({ prompt: [], abortSignal: undefined }))
|
||||
expect(result.content).toEqual([{ type: "text", text: "Simulation mock response." }])
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("simulation provider streams a default response when no script is queued", () =>
|
||||
Effect.gen(function* () {
|
||||
const provider = yield* Provider.Service
|
||||
const model = yield* provider.defaultModel().pipe(Effect.flatMap((item) => provider.getModel(item.providerID, item.modelID)))
|
||||
const language = yield* provider.getLanguage(model)
|
||||
|
||||
const result = yield* Effect.promise(() => language.doStream({ prompt: [], abortSignal: undefined }))
|
||||
const reader = result.stream.getReader()
|
||||
const parts: unknown[] = []
|
||||
while (true) {
|
||||
const next = yield* Effect.promise(() => reader.read())
|
||||
if (next.done) break
|
||||
parts.push(next.value)
|
||||
}
|
||||
|
||||
expect(parts).toContainEqual({ type: "text-delta", id: "simulation-text-1", delta: "Simulation mock response." })
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("simulation provider streams queued script actions", () =>
|
||||
Effect.gen(function* () {
|
||||
const simulation = yield* Simulation.Service
|
||||
|
||||
Reference in New Issue
Block a user