// 适配 `mizchi/llm@0.3.1`——生态里下载量最高的 MoonBit LLM 客户端。
//
// 该 SDK 的流式工具调用同样以文本增量交付参数:
//
// ToolCallStart(id~ : String, name~ : String)
// ToolCallDelta(id~ : String, input_delta~ : String)
// ToolCallEnd(id~ : String, name~ : String, input~ : Json)
//
// 它用**字符串 id** 标识调用,而 MoonStream 的 `CallKey` 用整数序号。
// 本适配器按 id 首次出现的顺序分配序号,因此同一条流里 id 与序号一一对应;
// `ToolCallEnd` 携带的 `input` 是 SDK 自己解析好的 Json,可用来对照本库的结果。
///|
/// `mizchi/llm` 流式事件到 MoonStream 会话的适配器。
pub struct MizchiLlmAdapter {
session : @session.Session
response_id : String
ids : Array[String]
}
///|
/// 创建适配器。
pub fn MizchiLlmAdapter::new(
response_id? : String,
) -> MizchiLlmAdapter raise @session.SessionError {
{
session: @session.Session::new(),
response_id: match response_id {
Some(value) => value
None => ""
},
ids: [],
}
}
///|
/// 底层会话。
pub fn MizchiLlmAdapter::session(self : MizchiLlmAdapter) -> @session.Session {
self.session
}
///|
/// 字符串 id 对应的整数序号(首次出现时分配)。
pub fn MizchiLlmAdapter::slot(self : MizchiLlmAdapter, id : String) -> Int {
for index, known in self.ids {
if known == id {
return index
}
}
self.ids.push(id)
self.ids.length() - 1
}
///|
/// 调用键。
pub fn MizchiLlmAdapter::key(
self : MizchiLlmAdapter,
slot : Int,
) -> @session.CallKey {
@session.CallKey::new(response=self.response_id, choice=0, index=slot)
}
///|
/// 处理一个 SDK 事件。
pub fn MizchiLlmAdapter::apply(
self : MizchiLlmAdapter,
event : @sdk.StreamEvent,
) -> @adapter.AdapterOutcome raise @session.SessionError {
match event {
@sdk.StreamEvent::ToolCallStart(id~, name~) => {
self.session.open(self.key(self.slot(id)), id=Some(id), name=Some(name))
@adapter.AdapterOutcome::Ignored
}
@sdk.StreamEvent::ToolCallDelta(id~, input_delta~) => {
let key = self.key(self.slot(id))
self.session.open(key, id=Some(id))
let events = self.session.feed(key, @utf8.encode(input_delta))
if events.is_empty() {
@adapter.AdapterOutcome::Ignored
} else {
@adapter.AdapterOutcome::Updates(updates=[
@adapter.CallUpdate::delta(key, events),
])
}
}
@sdk.StreamEvent::ToolCallEnd(id~, name~, ..) => {
self.session.open(self.key(self.slot(id)), id=Some(id), name=Some(name))
@adapter.AdapterOutcome::Ignored
}
@sdk.StreamEvent::MessageEnd(finish_reason~, ..) =>
self.finish_open(finish_reason.end_reason())
@sdk.StreamEvent::Error(_) =>
self.finish_open(@moonstream.EndReason::Truncated)
_ => @adapter.AdapterOutcome::Ignored
}
}
///|
fn @sdk.FinishReason::end_reason(
self : @sdk.FinishReason,
) -> @moonstream.EndReason {
match self {
Stop | ToolUse => @moonstream.EndReason::EndOfStream
Length | ContentFilter | Error | Unknown(_) =>
@moonstream.EndReason::Truncated
}
}
///|
fn MizchiLlmAdapter::finish_open(
self : MizchiLlmAdapter,
reason : @moonstream.EndReason,
) -> @adapter.AdapterOutcome {
let updates = @adapter.finish_open(self.session, reason)
if updates.is_empty() {
@adapter.AdapterOutcome::Ignored
} else {
@adapter.AdapterOutcome::Updates(updates~)
}
}
///|
/// 在没有收到 `ToolCallEnd` 的情况下结束某个调用。
pub fn MizchiLlmAdapter::finish_slot(
self : MizchiLlmAdapter,
slot : Int,
reason~ : @moonstream.EndReason,
) -> (Array[@moonstream.Event], @moonstream.FinishResult) raise @session.SessionError {
self.session.finish(self.key(slot), reason~)
}