Files
Cyrene_v2/internal/runtime/model/model.go
T
Aska 9c2a37c4ac
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat: 管理面板补齐(RAG导入/记忆添加/会话接history/诚实表述) + design/16 §4.6 IM多会话隔离+第三方身份+黑白名单
agent-98 面板批次:
- 会话管理面板改接 history.ListSessions(主web+次级) + 新建次级会话按钮
- 记忆面板手动添加入口 POST /admin/memories(owner)
- 知识库真正实现 IndexDocument(分块+落projectindex) + POST /admin/rag 导入端点
- 三面板空态诚实表述(去除'后端M10+提供')
- RAG嵌入降级到keyword/line(无直属embedder,不伪造语义,符合红线)

agent-99 design/16 §4.6:
- model.Message.SenderName + 迁移0006 sender_name 列
- 第三方身份边界: session.ResolveActor判定(非LLM猜) + [第三方·昵称]前缀(不改源content)
- 群聊/陌生人落独立sess-xxx(persistIsolated),不写主会话,不广播
- 黑/白名单(allowGroups/denyGroups/allowUsers/denyUsers)最前拦截,不进LLM
- 多账号 CHANNELS_JSON 配置 + 每平台多实例
- 管理只读 GET /admin/channels/{platform}/messages(该平台非主人消息流)

实机验证(JWT): 会话管理返回主会话web / 记忆添加成功 / RAG导入documents非空+Cyrene命中; 后端build/vet/test全绿 + 前端build绿
2026-09-07 05:13:36 +08:00

165 lines
6.2 KiB
Go

// Package model provides a vendor-agnostic LLM adapter layer for Cyrene v2.
//
// It defines a normalized Message/Request/Response vocabulary, a single
// Adapter interface implemented by OpenAIChatAdapter (which speaks the
// OpenAI-compatible /chat/completions protocol used by OpenAI, DashScope/百炼,
// local gateways and proxies), a per-role Router, an automatic fallback Chain,
// multimodal (vision) detection, and per-call CallRecord accounting.
//
// The full tool loop (schema registration, argument binding, result re-feed) is
// deferred to M2; this package only carries interface-level placeholders
// (ToolSpec) so the wire contract is ready without executing any loop.
package model
import "time"
// Role identifies who authored a message. The values are vendor-agnostic and
// translated to each provider's own role vocabulary by the adapter.
type Role string
const (
// RoleSystem is a system/instruction message.
RoleSystem Role = "system"
// RoleUser is an end-user message.
RoleUser Role = "user"
// RoleAssistant is an assistant (model) message.
RoleAssistant Role = "assistant"
// RoleTool is a tool-result message (reserved for the M2 tool loop).
RoleTool Role = "tool"
)
// ContentType describes the kind of payload inside a Content part.
type ContentType string
const (
// ContentTypeText is a plain-text content part.
ContentTypeText ContentType = "text"
// ContentTypeImage is an image content part.
ContentTypeImage ContentType = "image_url"
)
// Content is a single, possibly multimodal, part of a message.
type Content struct {
// Type distinguishes a text part from an image part.
Type ContentType
// Text is the text payload when Type == ContentTypeText.
Text string
// ImageURL is the image source when Type == ContentTypeImage. It may be a
// data: URI or an http(s):// URL.
ImageURL string
}
// Message is a single conversation turn, normalized across providers.
type Message struct {
Role Role
// Content carries one or more parts. A text-only message uses a single
// ContentTypeText part.
Content []Content
// SenderName is the human-readable display name of the message's sender
// (design/16 §4.6). It is transport metadata used by the context-assembly
// layer to label third-party messages for the model (e.g. "[第三方 · 昵称]"),
// never a substitute for the strongly typed sender identity carried on the
// channel layer. Empty means the sender is unlabeled (the owner, or an
// unknown/unresolved sender), and callers may leave it unset.
SenderName string
// ToolCallID and ToolCallName are reserved as interface-level placeholders
// for the M2 tool loop; they are ignored by current adapters.
ToolCallID string
ToolCallName string
}
// TextMessage builds a text-only message.
func TextMessage(role Role, text string) Message {
return Message{Role: role, Content: []Content{{Type: ContentTypeText, Text: text}}}
}
// ImageMessage builds a message carrying optional text plus an image. imageURL
// may be a data: URI or an http(s):// URL. An empty text omits the text part.
func ImageMessage(role Role, text, imageURL string) Message {
parts := make([]Content, 0, 2)
if text != "" {
parts = append(parts, Content{Type: ContentTypeText, Text: text})
}
parts = append(parts, Content{Type: ContentTypeImage, ImageURL: imageURL})
return Message{Role: role, Content: parts}
}
// ToolSpec is an interface-level placeholder for a tool/function schema. The
// full tool loop (registration, argument binding, result re-feed) is M2; here
// it only lets a request advertise tools to the provider in the wire contract.
type ToolSpec struct {
Name string
Description string
// Parameters is the JSON Schema for the tool arguments. Populated from M2.
Parameters any
}
// ToolCall is a single assistant-originated tool invocation parsed from a
// provider's tool_use content block. Args is the structured argument object
// (never a serialized JSON string), so a tool loop can bind it directly without
// re-parsing.
type ToolCall struct {
// Name is the tool being invoked.
Name string
// Args are the tool arguments, decoded from the provider's structured
// tool_use.input object. It is never a JSON-encoded string.
Args map[string]any
}
// Request is a completion request, normalized across providers.
type Request struct {
// Model overrides the adapter's configured default model. Empty means the
// adapter default (or the chain step model) applies.
Model string
Messages []Message
// Temperature is the sampling temperature. nil means the provider default.
Temperature *float64
// MaxTokens caps the generated tokens. Zero means the provider default.
MaxTokens int
// Purpose labels the call for CallRecord accounting (e.g. "chat").
Purpose string
// Tools is the interface-level placeholder for M2 function calling.
Tools []ToolSpec
// ToolChoice optionally directs how the provider selects a tool. It is an
// opaque provider-agnostic directive (e.g. {"type":"auto"} or {"type":"tool",
// "name":"x"}); empty means the provider default. Passed through verbatim to
// protocols that support it (Anthropic's tool_choice).
ToolChoice any
}
// Response is a normalized completion result.
type Response struct {
// Model is the model that produced the response.
Model string
Content string
// Message is the normalized assistant message.
Message Message
// ToolCalls carries any tool_use blocks the assistant emitted. Empty when the
// response is pure text.
ToolCalls []ToolCall
// PromptTokens, CompletionTokens and TotalTokens report provider usage when
// available; zero when the provider does not return it.
PromptTokens int
CompletionTokens int
TotalTokens int
}
// CallRecord describes a single successfully completed LLM call, for the M9
// monitor/frontend. A failed attempt emits no record.
type CallRecord struct {
// Model is the model that served the request.
Model string
// Purpose labels what the call was for.
Purpose string
// Tokens is the total token consumption reported by the provider.
Tokens int
// TookMs is the end-to-end call latency in milliseconds.
TookMs int64
}
// Options carries per-call knobs layered on top of the Request.
type Options struct {
// Timeout bounds a single HTTP call. Zero uses the adapter client timeout.
Timeout time.Duration
}