Runtime-independent typed LLM nodes for workgraph
///|
async test "workgraph-llm response usage" {
let entry = @core.NodeId::NodeId("llm")
let provider = @llm.MockProvider::new([
[
@llm.MessageStart,
@llm.TextDelta("Use a typed node"),
@llm.MessageEnd(finish_reason=@llm.Stop, usage=None),
],
]).boxed()
let node = @workgraph_llm.llm_node(
entry,
@core.NodeMetadata::NodeMetadata(
name="LLM",
description=None,
kind=@core.Llm,
tags=[],
),
@workgraph_llm.LlmNodeSpec::LlmNodeSpec(
provider,
fn(_context, prompt : String) {
@workgraph_llm.LlmRequest::LlmRequest([@llm.Message::user(prompt)])
},
fn(_state, response) {
@core.NodeOutput::NodeOutput(Some(response.text), None)
},
),
)
let definition = @core.GraphDefinition::GraphDefinition(
@core.Reducer::Reducer(fn(_state : String, patch : String) { patch }),
)
definition.add_node(node)
definition.set_router(
entry,
@core.router([], fn(_state, _completion) { @core.End }),
)
definition.set_entry(entry)
let result = @core.GraphRuntime::GraphRuntime(definition.compile()).invoke(
"Plan the next step",
)
inspect(result.final_state, content="Use a typed node")
inspect(result.steps, content="1")
}pub struct LlmNodeSpec[S, P] {
provider : BoxedProvider
build_request : (NodeContext, S) -> LlmRequest raise
decode_response : (S, CollectResult) -> NodeOutput[P] raise
}fn[S, P] LlmNodeSpec::LlmNodeSpec(provider : BoxedProvider, build_request : (NodeContext, S) -> LlmRequest raise, decode_response : (S, CollectResult) -> NodeOutput[P] raise) -> LlmNodeSpec[S, P]