// Session:把多个 tool call 的参数片段路由到互相独立的解析器。
//
// 一次响应里可能同时出现多个 tool call,参数片段还会交错到达。Session 用
// 复合键(response / choice / index)分流,保证两条流不会被拼成同一个对象。
///|
/// 一个 tool call 的稳定复合键。
///
/// 只用 `index` 不够:同一响应里不同 choice 可以各有 index 0,不同响应也会重号。
pub(all) struct CallKey {
/// 响应或流的标识;调用方没有提供时用空字符串。
response : String
/// choice 序号。
choice : Int
/// 该 choice 内的 tool call 序号。
index : Int
} derive(Eq, Debug, ToJson)
///|
pub fn CallKey::new(response? : String, choice? : Int, index? : Int) -> CallKey {
{
response: match response {
Some(value) => value
None => ""
},
choice: match choice {
Some(value) => value
None => 0
},
index: match index {
Some(value) => value
None => 0
},
}
}
///|
/// 路由层面的错误。适配层应当把它们当作"输入流组织方式不对"来报告,
/// 而不是当作模型输出不合法。
pub(all) suberror SessionError {
/// 该调用还没有起始上下文(没有先 `open`)。
UnknownCall(key~ : CallKey)
/// 同一调用被赋予了不同的 id。
CallIdConflict(key~ : CallKey, existing~ : String, incoming~ : String)
/// 同一调用被赋予了不同的函数名。
CallNameConflict(key~ : CallKey, existing~ : String, incoming~ : String)
/// 会话的限制参数非法。包装主库的 `InvalidLimits`,使本包只有一个错误通道。
InvalidLimits(error~ : @moonstream.ParseError)
/// 该调用自身的解析失败。
ParseFailed(key~ : CallKey, error~ : @moonstream.ParseError)
} derive(Eq, Debug, ToJson)
///|
/// 一个已打开的调用。
priv struct CallEntry {
key : CallKey
parser : @moonstream.Parser
mut id : String?
mut name : String?
mut state : CallState
}
///|
priv enum CallState {
Open
Failed(@moonstream.ParseError)
Finished
}
///|
/// 多 tool call 会话。
pub struct Session {
inner : SessionState
}
///|
/// 会话的全部状态。私有实现细节,不是可用 API。
struct SessionState {
limits : @moonstream.Limits
calls : Array[CallEntry]
}
///|
/// 创建会话。限制在创建时校验,之后传给每个调用的解析器。
///
/// **限制是每个调用各自计数的**:`max_total_bytes` 约束的是单个调用的参数字节数,
/// 不是整个会话的累计量。会话里能开多少调用由调用方自己控制。
///
/// 限制非法时抛 `SessionError::InvalidLimits`(本包只有一个错误通道)。
pub fn Session::new(limits? : @moonstream.Limits) -> Session raise SessionError {
let limits = match limits {
Some(value) => value
None => @moonstream.Limits::strict()
}
// 用一次构造校验限制;解析器各自也会再校验。
try @moonstream.Parser::new(limits~) catch {
err => raise SessionError::InvalidLimits(error=err)
} noraise {
_ => ()
}
{ inner: { limits, calls: [] } }
}
///|
/// 注册一个调用的起始上下文。
///
/// `id` 与 `name` 是带默认值的标签参数,类型都是 `String?`,因此适配层可以直接透传
/// SDK 的 Option 字段:`open(key, id=delta.id, name=delta.name)`。
/// 重复注册同一个键是允许的(id / name 可以只在第一帧出现),
/// 但 id 或函数名冲突会报错,避免把两个不同调用混在一起。
pub fn Session::open(
self : Session,
key : CallKey,
id? : String? = None,
name? : String? = None,
) -> Unit raise SessionError {
let entry = match self.find(key) {
Some(entry) => entry
None => {
let parser = @moonstream.Parser::new(limits=self.inner.limits) catch {
err => raise SessionError::ParseFailed(key~, error=err)
}
let entry = { key, parser, id: None, name: None, state: Open }
self.inner.calls.push(entry)
entry
}
}
// 先完成全部冲突校验,再写入元数据,避免 name 冲突留下半更新状态。
match id {
Some(incoming) =>
match entry.id {
Some(existing) =>
if existing != incoming {
raise SessionError::CallIdConflict(key~, existing~, incoming~)
}
None => ()
}
None => ()
}
match name {
Some(incoming) =>
match entry.name {
Some(existing) =>
if existing != incoming {
raise SessionError::CallNameConflict(key~, existing~, incoming~)
}
None => ()
}
None => ()
}
if entry.id is None {
entry.id = id
}
if entry.name is None {
entry.name = name
}
}
///|
/// 把一段参数片段交给该调用自己的解析器。
pub fn Session::feed(
self : Session,
key : CallKey,
bytes : Bytes,
) -> Array[@moonstream.Event] raise SessionError {
let entry = match self.find(key) {
Some(entry) => entry
None => raise SessionError::UnknownCall(key~)
}
match entry.state {
Failed(error) => raise SessionError::ParseFailed(key~, error~)
Finished =>
raise SessionError::ParseFailed(
key~,
error=@moonstream.ParseError::AlreadyFinished(op="feed"),
)
Open => ()
}
try entry.parser.feed(bytes) catch {
err => {
entry.state = Failed(err)
raise SessionError::ParseFailed(key~, error=err)
}
} noraise {
events => events
}
}
///|
/// 结束该调用,返回它的收尾事件与结果。其它调用不受影响。
pub fn Session::finish(
self : Session,
key : CallKey,
reason? : @moonstream.EndReason,
) -> (Array[@moonstream.Event], @moonstream.FinishResult) raise SessionError {
let entry = match self.find(key) {
Some(entry) => entry
None => raise SessionError::UnknownCall(key~)
}
match entry.state {
Failed(error) => raise SessionError::ParseFailed(key~, error~)
Finished =>
raise SessionError::ParseFailed(
key~,
error=@moonstream.ParseError::AlreadyFinished(op="finish"),
)
Open => ()
}
try entry.parser.finish(reason?) catch {
err => {
entry.state = Failed(err)
raise SessionError::ParseFailed(key~, error=err)
}
} noraise {
outcome => {
entry.state = Finished
outcome
}
}
}
///|
/// 该调用是否仍可继续接收输入。
pub fn Session::is_open(self : Session, key : CallKey) -> Bool {
match self.find(key) {
Some(entry) => entry.state is Open
None => false
}
}
///|
/// 该调用是否已经 `finish`。
pub fn Session::is_finished(self : Session, key : CallKey) -> Bool {
match self.find(key) {
Some(entry) => entry.state is Finished
None => false
}
}
///|
/// 该调用是否因解析错误进入不可恢复状态。
pub fn Session::is_failed(self : Session, key : CallKey) -> Bool {
match self.find(key) {
Some(entry) => entry.state is Failed(_)
None => false
}
}
///|
/// 返回使该调用终止的原始解析错误。
pub fn Session::failure(
self : Session,
key : CallKey,
) -> @moonstream.ParseError? {
match self.find(key) {
Some({ state: Failed(error), .. }) => Some(error)
_ => None
}
}
///|
/// 已注册调用的数量(含已结束的)。
pub fn Session::call_count(self : Session) -> Int {
self.inner.calls.length()
}
///|
/// 全部已注册的调用键(含已结束的),按注册顺序。
pub fn Session::calls(self : Session) -> Array[CallKey] {
let out = []
for entry in self.inner.calls {
out.push(entry.key)
}
out
}
///|
/// 仍可继续接收输入的调用键,按注册顺序;失败与已结束调用不包含在内。
pub fn Session::open_calls(self : Session) -> Array[CallKey] {
let out = []
for entry in self.inner.calls {
if entry.state is Open {
out.push(entry.key)
}
}
out
}
///|
/// 该调用已经消费的字节数。
pub fn Session::offset(self : Session, key : CallKey) -> Int? {
match self.find(key) {
Some(entry) => Some(entry.parser.offset())
None => None
}
}
///|
/// 该调用已知的 id。
pub fn Session::call_id(self : Session, key : CallKey) -> String? {
match self.find(key) {
Some(entry) => entry.id
None => None
}
}
///|
/// 该调用已知的函数名。
pub fn Session::call_name(self : Session, key : CallKey) -> String? {
match self.find(key) {
Some(entry) => entry.name
None => None
}
}
///|
/// 按复合键查找调用。
fn Session::find(self : Session, key : CallKey) -> CallEntry? {
for entry in self.inner.calls {
if entry.key == key {
return Some(entry)
}
}
None
}