mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 17:38:10 +00:00
feat(provider): complete ChatStream with llmsproxy-grade streaming
Rewrite LuaAdaptedProvider.ChatStream to match the maturity of
llmsproxy's streaming implementation:
HTTP layer:
- Dedicated stream HTTP client with no overall timeout (SSE must not
be cut by the 180s Chat timeout); only a 30s dial timeout
- Uses applyAdapterHeaders (supports build_headers dynamic signing
hook), matching the non-streaming Chat path
Non-200 response handling:
- New TransformError Lua hook (adapter.transform_error) for per-source
protocol knowledge in error messages
- Safe fallback truncation of raw error bodies (prevents HTML dump
leakage to clients)
SSE parsing enhancements:
- parseOpenAICompatibleStreamChunkFull: handles token usage in the
final chunk (prompt_tokens/prompt, total_tokens/total dual keys),
prompt cache detail fields, and empty-string finish_reason filtering
(sensenova sends "" on every chunk)
- Replaced old SSEScanner with bufio.Scanner (larger buffer, fewer
allocations)
Stream integrity:
- errorOnlyChunk detection: holds back the first chunk to reject
degenerate streams (e.g. zen free pool's finish_reason:"network_error"
with empty content) before any byte reaches the caller
- [DONE] dedup: adapters that already emit a terminating done chunk
with the real finish_reason don't get a second reason-less done
- Clean EOF sends a final Done:true if no done was seen
Struct changes:
- StreamChunk: added FinishReason and Usage fields for callers
- LuaAdaptedProvider: added streamClient (lazy) + streamMu
Tested: curl against llmsproxy SSE confirms reasoning_content parsing
is correct (delta.reasoning_content), usage chunk handling works, and
[DONE] termination is properly emitted.
This commit is contained in:
@ -335,6 +335,42 @@ func (v *VM) CallTransformResponse(name, rawJSON string) (string, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// TransformError 执行可选的 adapter.transform_error(status, body) 钩子。
|
||||
// 返回 reason: 适配器提取的错误原因;ok=false 表示适配器未定义此钩子。
|
||||
func (v *VM) TransformError(name string, status int, body string) (reason string, ok bool, err error) {
|
||||
p := v.pool(name)
|
||||
if p == nil {
|
||||
return "", false, nil
|
||||
}
|
||||
w, err := p.acquire()
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
defer p.release(w)
|
||||
L := w.L
|
||||
adapter := L.GetGlobal(adapterGlobal)
|
||||
tbl, ok2 := adapter.(*lua.LTable)
|
||||
if !ok2 {
|
||||
return "", false, nil
|
||||
}
|
||||
f := tbl.RawGetString("transform_error")
|
||||
if _, ok := f.(*lua.LFunction); !ok {
|
||||
return "", false, nil
|
||||
}
|
||||
L.Push(f)
|
||||
L.Push(lua.LNumber(status))
|
||||
L.Push(lua.LString(body))
|
||||
if err := L.PCall(2, 1, nil); err != nil {
|
||||
return "", false, fmt.Errorf("transform_error: %w", err)
|
||||
}
|
||||
res := L.Get(-1)
|
||||
L.Pop(1)
|
||||
if res.Type() != lua.LTString {
|
||||
return "", false, nil
|
||||
}
|
||||
return res.String(), true, nil
|
||||
}
|
||||
|
||||
func (v *VM) CallTransformStreamChunk(name, rawLine string) (string, error) {
|
||||
p := v.pool(name)
|
||||
if p == nil {
|
||||
|
||||
Reference in New Issue
Block a user