WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,426 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultMaxContextTokens is the context window assumed for a model that does
|
||||
// not declare one. 200k matches typical windows on current chat/VLM models
|
||||
// (GPT-4.1, Claude, Gemini, Qwen, etc.).
|
||||
//
|
||||
// It is still below the largest windows on the market. Guessing high is
|
||||
// the dangerous direction: compaction never fires, and the first sign of
|
||||
// trouble is the provider rejecting the request. Guessing low only costs some
|
||||
// history that would have fit.
|
||||
const DefaultMaxContextTokens = 200000
|
||||
|
||||
// AgentMaxContextTokens picks the context window for one agent run. An
|
||||
// explicit agent setting wins, then whatever the model declares, then the
|
||||
// assumed default.
|
||||
func AgentMaxContextTokens(configured, modelContextWindow int) int {
|
||||
if configured > 0 {
|
||||
return configured
|
||||
}
|
||||
if modelContextWindow > 0 {
|
||||
return modelContextWindow
|
||||
}
|
||||
return DefaultMaxContextTokens
|
||||
}
|
||||
|
||||
// DefaultSmartReasoningMaxCompletionTokens is the per-round budget when the
|
||||
// agent cannot emit write_sandbox_file / edit_sandbox_file. 4096 matches
|
||||
// typical OpenAI-compatible provider defaults and is enough for ordinary
|
||||
// tool-call JSON.
|
||||
const DefaultSmartReasoningMaxCompletionTokens = 4096
|
||||
|
||||
// DefaultAgentMaxCompletionTokens is the per-round budget when the agent
|
||||
// can write or edit sandbox files. Those tool calls carry the file body in
|
||||
// JSON; a 4096 cap truncates mid-stream (finish_reason=length).
|
||||
const DefaultAgentMaxCompletionTokens = 24576
|
||||
|
||||
// DefaultQuickAnswerMaxCompletionTokens is the RAG answer budget.
|
||||
const DefaultQuickAnswerMaxCompletionTokens = 2048
|
||||
|
||||
// NeedsSandboxWriteCompletionBudget reports whether this agent may register
|
||||
// write_sandbox_file / edit_sandbox_file. Those tools follow the bound
|
||||
// sandbox, not the allowed_tools checklist.
|
||||
func NeedsSandboxWriteCompletionBudget(sandboxConfigID string) bool {
|
||||
return strings.TrimSpace(sandboxConfigID) != ""
|
||||
}
|
||||
|
||||
// DefaultMaxCompletionTokens is the unset-config default for one agent:
|
||||
// quick-answer 2048, smart-reasoning 4096, smart-reasoning with a sandbox 24576.
|
||||
func DefaultMaxCompletionTokens(agentMode, sandboxConfigID string) int {
|
||||
if agentMode == AgentModeSmartReasoning {
|
||||
if NeedsSandboxWriteCompletionBudget(sandboxConfigID) {
|
||||
return DefaultAgentMaxCompletionTokens
|
||||
}
|
||||
return DefaultSmartReasoningMaxCompletionTokens
|
||||
}
|
||||
return DefaultQuickAnswerMaxCompletionTokens
|
||||
}
|
||||
|
||||
// AgentRoundMaxCompletionTokens returns the completion-token budget for one
|
||||
// ReAct LLM round when no sandbox is bound. Zero (unset) uses
|
||||
// DefaultSmartReasoningMaxCompletionTokens. An explicit configured value is
|
||||
// honored as-is.
|
||||
func AgentRoundMaxCompletionTokens(configured int) int {
|
||||
return AgentRoundMaxCompletionTokensFor(configured, "")
|
||||
}
|
||||
|
||||
// MinSandboxWriteCompletionTokens is the floor applied to an explicitly
|
||||
// configured per-round budget when a sandbox is bound. write_sandbox_file and
|
||||
// edit_sandbox_file carry the whole file body inside the tool-call JSON, so a
|
||||
// cap sized for ordinary chat truncates the arguments mid-string on every
|
||||
// attempt — the agent then burns its rounds rewriting a file it can never
|
||||
// finish. Below this the setting is not a preference, it is a deadlock.
|
||||
const MinSandboxWriteCompletionTokens = 8192
|
||||
|
||||
// AgentRoundMaxCompletionTokensFor is AgentRoundMaxCompletionTokens with
|
||||
// the agent's sandbox: unset + sandbox uses the large write-file budget, and a
|
||||
// configured value too small to hold a file write is raised to the floor.
|
||||
func AgentRoundMaxCompletionTokensFor(configured int, sandboxConfigID string) int {
|
||||
if configured > 0 {
|
||||
if NeedsSandboxWriteCompletionBudget(sandboxConfigID) &&
|
||||
configured < MinSandboxWriteCompletionTokens {
|
||||
return MinSandboxWriteCompletionTokens
|
||||
}
|
||||
return configured
|
||||
}
|
||||
return DefaultMaxCompletionTokens(AgentModeSmartReasoning, sandboxConfigID)
|
||||
}
|
||||
|
||||
// UnlimitedMaxIterations is the stored MaxIterations value meaning the ReAct
|
||||
// loop has no round cap. Zero still means "unset, apply default" so agents
|
||||
// that omit the field keep their current behaviour.
|
||||
const UnlimitedMaxIterations = -1
|
||||
|
||||
// AgentConfig represents the full agent configuration (used at tenant level and runtime)
|
||||
// This includes all configuration parameters for agent execution
|
||||
type AgentConfig struct {
|
||||
// MaxIterations caps ReAct rounds. Zero is unset (filled with a default);
|
||||
// a negative value is unlimited — the loop runs until the model stops,
|
||||
// the user cancels, or another guard fires. See UnlimitedMaxIterations.
|
||||
MaxIterations int `json:"max_iterations"`
|
||||
AllowedTools []string `json:"allowed_tools"` // List of allowed tool names
|
||||
Temperature float64 `json:"temperature"` // LLM temperature for agent
|
||||
KnowledgeBases []string `json:"knowledge_bases"` // Accessible knowledge base IDs
|
||||
KnowledgeIDs []string `json:"knowledge_ids"` // Accessible knowledge IDs (individual documents)
|
||||
SystemPrompt string `json:"system_prompt,omitempty"` // Unified system prompt (uses web_search_status placeholder for dynamic behavior)
|
||||
InstructionSkillPrompt string `json:"-"` // Resolved runtime-only Markdown rules, appended to the base prompt.
|
||||
// Deprecated: Use SystemPrompt instead. Kept for backward compatibility during migration.
|
||||
SystemPromptWebEnabled string `json:"system_prompt_web_enabled,omitempty"` // Deprecated: Custom prompt when web search is enabled
|
||||
SystemPromptWebDisabled string `json:"system_prompt_web_disabled,omitempty"` // Deprecated: Custom prompt when web search is disabled
|
||||
UseCustomSystemPrompt bool `json:"use_custom_system_prompt"` // Whether to use custom system prompt instead of default
|
||||
WebSearchEnabled bool `json:"web_search_enabled"` // Whether web search tool is enabled
|
||||
WebSearchMaxResults int `json:"web_search_max_results"` // Maximum number of web search results (default: 5)
|
||||
WebSearchProviderID string `json:"web_search_provider_id,omitempty"` // WebSearchProviderEntity ID (resolved from agent config)
|
||||
MultiTurnEnabled bool `json:"multi_turn_enabled"` // Whether multi-turn conversation is enabled
|
||||
HistoryTurns int `json:"history_turns"` // Number of history turns to keep in context
|
||||
MemoryEnabled *bool `json:"memory_enabled,omitempty"` // nil inherits workspace
|
||||
SearchTargets SearchTargets `json:"-"` // Pre-computed unified search targets (runtime only)
|
||||
// MCP service selection
|
||||
MCPSelectionMode string `json:"mcp_selection_mode"` // MCP selection mode: "all", "selected", "none"
|
||||
MCPServices []string `json:"mcp_services"` // Selected MCP service IDs (when mode is "selected")
|
||||
// MCPAuthWaitTimeout is how many seconds an agent run waits for
|
||||
// in-conversation OAuth authorization before skipping. <=0 falls back to
|
||||
// the gate's configured timeout. The wait is always bounded (no leak).
|
||||
MCPAuthWaitTimeout int `json:"mcp_auth_wait_timeout,omitempty"`
|
||||
// Whether to enable thinking mode (for models that support extended thinking)
|
||||
Thinking *bool `json:"thinking"`
|
||||
// Whether final answers include knowledge/web source citations. Nil defaults to true.
|
||||
CitationEnabled *bool `json:"citation_enabled"`
|
||||
// Whether to retrieve knowledge base only when explicitly mentioned with @ (default: false)
|
||||
RetrieveKBOnlyWhenMentioned bool `json:"retrieve_kb_only_when_mentioned"`
|
||||
|
||||
// Whether to retain retrieval history (like wiki_read_page results) across turns (default: false)
|
||||
RetainRetrievalHistory bool `json:"retain_retrieval_history"`
|
||||
|
||||
// Skills configuration (Progressive Disclosure pattern)
|
||||
SkillsEnabled bool `json:"skills_enabled"` // Whether skills are enabled (default: false)
|
||||
SkillDirs []string `json:"skill_dirs"` // Directories to search for skills
|
||||
AllowedSkills []string `json:"allowed_skills"` // Skill names whitelist (empty = allow all)
|
||||
|
||||
// Runtime-only fields (not persisted)
|
||||
VLMModelID string `json:"-"` // VLM model ID for tool result image analysis (set from CustomAgent config)
|
||||
SandboxConfigID string `json:"-"` // Workspace sandbox config ID for skill execution (set from CustomAgent config)
|
||||
// TenantSkills are the skills installed into the selected sandbox config's
|
||||
// snapshot image, already narrowed to the ones this run can actually
|
||||
// invoke. Runtime only: it is derived per turn from the config the agent
|
||||
// selected, never stored on the agent record.
|
||||
TenantSkills []*TenantSkillEntity `json:"-"`
|
||||
// Per-request @mention pins (runtime only; injected as <must_use> in the user message).
|
||||
PinnedMCPServiceIDs []string `json:"-"`
|
||||
PinnedSkillNames []string `json:"-"`
|
||||
// SharedAgentReadOnly prevents a shared agent from mutating resources in
|
||||
// its source workspace. It is set from the verified share relation, never
|
||||
// inferred from a client-provided tenant ID.
|
||||
SharedAgentReadOnly bool `json:"-"`
|
||||
// LLM call timeout in seconds (default: 120). Controls the maximum time for a single LLM call.
|
||||
LLMCallTimeout int `json:"llm_call_timeout,omitempty"`
|
||||
|
||||
// Maximum completion tokens for each ReAct LLM round. Zero means
|
||||
// DefaultMaxCompletionTokens for this agent's mode and sandbox.
|
||||
// Explicit values are sent as-is.
|
||||
MaxCompletionTokens int `json:"max_completion_tokens,omitempty"`
|
||||
|
||||
// Maximum character length for tool output (default: 16000).
|
||||
// Outputs exceeding this limit are truncated with head + tail preservation.
|
||||
MaxToolOutputChars int `json:"max_tool_output_chars,omitempty"`
|
||||
|
||||
// Maximum context window tokens for the agent. Zero means "use the
|
||||
// model's context_window, or DefaultMaxContextTokens (200000)".
|
||||
MaxContextTokens int `json:"max_context_tokens,omitempty"`
|
||||
|
||||
// How much recent conversation a compaction keeps verbatim. Zero means
|
||||
// compaction.DefaultKeepRecentTokens, scaled down on small windows. This
|
||||
// is the knob that decides how often compaction runs: whatever the context
|
||||
// grew to, it comes out of a compaction at roughly this size.
|
||||
CompactionKeepRecentTokens int `json:"compaction_keep_recent_tokens,omitempty"`
|
||||
|
||||
// Whether to execute independent tool calls in parallel (default: false).
|
||||
// When enabled and the LLM returns multiple tool calls, they run concurrently via errgroup.
|
||||
ParallelToolCalls bool `json:"parallel_tool_calls,omitempty"`
|
||||
|
||||
// skillInstallMode routes this run's shell_exec to the privileged
|
||||
// install-mode executor (root, skills image root writable). It is
|
||||
// unexported on purpose: JSON cannot reach it, so no stored agent record
|
||||
// and no API payload can request the privilege, and no other package can
|
||||
// assign it. EnableSkillInstallMode is the only way in and it refuses
|
||||
// every agent except the built-in skill installer.
|
||||
skillInstallMode bool
|
||||
|
||||
// skillInstallDir is the one skill directory this install owns. The skill
|
||||
// file tools are scoped to it, so an installer cannot write a neighbouring
|
||||
// skill in the shared image. Unexported for the same reason as
|
||||
// skillInstallMode: the scope of a privilege must not be settable by
|
||||
// anything a tenant can store or send.
|
||||
skillInstallDir string
|
||||
}
|
||||
|
||||
// EnableSkillInstallMode grants install-mode shell execution to the built-in
|
||||
// skill installer agent and to nothing else, scoped to the skill directory it
|
||||
// was started for. The agent ID is checked here rather than at the call site
|
||||
// so there is exactly one place to audit.
|
||||
//
|
||||
// The privilege and its scope are granted together: a caller cannot obtain the
|
||||
// root shell without naming the directory the file tools will be confined to.
|
||||
// skillDir is stored verbatim and validated where the tools are constructed —
|
||||
// types cannot import the sandbox package that owns the path rules.
|
||||
func (c *AgentConfig) EnableSkillInstallMode(agentID, skillDir string) {
|
||||
if c == nil || agentID != BuiltinSkillInstallerID {
|
||||
return
|
||||
}
|
||||
c.skillInstallMode = true
|
||||
c.skillInstallDir = skillDir
|
||||
}
|
||||
|
||||
// SkillInstallMode reports whether this run may use the privileged
|
||||
// install-mode shell.
|
||||
func (c *AgentConfig) SkillInstallMode() bool {
|
||||
return c != nil && c.skillInstallMode
|
||||
}
|
||||
|
||||
// SkillInstallDir is the skill directory this install may write, or "" when
|
||||
// this run is not an install.
|
||||
func (c *AgentConfig) SkillInstallDir() string {
|
||||
if c == nil || !c.skillInstallMode {
|
||||
return ""
|
||||
}
|
||||
return c.skillInstallDir
|
||||
}
|
||||
|
||||
// UnlimitedIterations reports whether the ReAct loop has no round cap.
|
||||
func (c *AgentConfig) UnlimitedIterations() bool {
|
||||
return c != nil && c.MaxIterations < 0
|
||||
}
|
||||
|
||||
// CitationsEnabled preserves citation output for legacy runtime configs that
|
||||
// predate the setting and therefore have a nil CitationEnabled value.
|
||||
func (c *AgentConfig) CitationsEnabled() bool {
|
||||
return c == nil || c.CitationEnabled == nil || *c.CitationEnabled
|
||||
}
|
||||
|
||||
// SessionAgentConfig represents session-level agent configuration
|
||||
// Sessions only store Enabled and KnowledgeBases; other configs are read from Tenant at runtime
|
||||
type SessionAgentConfig struct {
|
||||
AgentModeEnabled bool `json:"agent_mode_enabled"` // Whether agent mode is enabled for this session
|
||||
WebSearchEnabled bool `json:"web_search_enabled"` // Whether web search is enabled for this session
|
||||
KnowledgeBases []string `json:"knowledge_bases"` // Accessible knowledge base IDs for this session
|
||||
KnowledgeIDs []string `json:"knowledge_ids"` // Accessible knowledge IDs (individual documents) for this session
|
||||
}
|
||||
|
||||
// Value implements driver.Valuer interface for AgentConfig
|
||||
func (c AgentConfig) Value() (driver.Value, error) {
|
||||
return json.Marshal(c)
|
||||
}
|
||||
|
||||
// Scan implements sql.Scanner interface for AgentConfig
|
||||
func (c *AgentConfig) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
var b []byte
|
||||
switch v := value.(type) {
|
||||
case []byte:
|
||||
b = v
|
||||
case string:
|
||||
b = []byte(v)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(b, c)
|
||||
}
|
||||
|
||||
// Value implements driver.Valuer interface for SessionAgentConfig
|
||||
func (c SessionAgentConfig) Value() (driver.Value, error) {
|
||||
return json.Marshal(c)
|
||||
}
|
||||
|
||||
// Scan implements sql.Scanner interface for SessionAgentConfig
|
||||
func (c *SessionAgentConfig) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
var b []byte
|
||||
switch v := value.(type) {
|
||||
case []byte:
|
||||
b = v
|
||||
case string:
|
||||
b = []byte(v)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(b, c)
|
||||
}
|
||||
|
||||
// ResolveSystemPrompt returns the prompt template for the given web search state.
|
||||
// It uses the unified SystemPrompt field, falling back to deprecated fields for backward compatibility.
|
||||
func (c *AgentConfig) ResolveSystemPrompt(webSearchEnabled bool) string {
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// First, try the new unified SystemPrompt field
|
||||
if c.SystemPrompt != "" {
|
||||
return c.SystemPrompt
|
||||
}
|
||||
|
||||
// Fallback to deprecated fields for backward compatibility
|
||||
if webSearchEnabled {
|
||||
if c.SystemPromptWebEnabled != "" {
|
||||
return c.SystemPromptWebEnabled
|
||||
}
|
||||
} else {
|
||||
if c.SystemPromptWebDisabled != "" {
|
||||
return c.SystemPromptWebDisabled
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// Tool defines the interface that all agent tools must implement
|
||||
type Tool interface {
|
||||
// Name returns the unique identifier for this tool
|
||||
Name() string
|
||||
|
||||
// Description returns a human-readable description of what the tool does
|
||||
Description() string
|
||||
|
||||
// Parameters returns the JSON Schema for the tool's parameters
|
||||
Parameters() json.RawMessage
|
||||
|
||||
// Execute runs the tool with the given arguments
|
||||
Execute(ctx context.Context, args json.RawMessage) (*ToolResult, error)
|
||||
}
|
||||
|
||||
// Cleanable is an optional interface that tools can implement to release resources.
|
||||
// Tools implementing this interface will have their Cleanup method called during
|
||||
// registry cleanup (e.g., at the end of an agent session).
|
||||
type Cleanable interface {
|
||||
Cleanup(ctx context.Context)
|
||||
}
|
||||
|
||||
// ToolResult represents the result of a tool execution
|
||||
type ToolResult struct {
|
||||
Success bool `json:"success"` // Whether the tool executed successfully
|
||||
Output string `json:"output"` // Human-readable output
|
||||
Data map[string]interface{} `json:"data,omitempty"` // Structured data for programmatic use
|
||||
Error string `json:"error,omitempty"` // Error message if execution failed
|
||||
Images []string `json:"images,omitempty"` // Base64 data URIs from tool (e.g. MCP image content)
|
||||
}
|
||||
|
||||
// ToolCall represents a single tool invocation within an agent step
|
||||
type ToolCall struct {
|
||||
ID string `json:"id"` // Function call ID from LLM
|
||||
Name string `json:"name"` // Tool name
|
||||
Args map[string]interface{} `json:"args"` // Tool arguments
|
||||
Result *ToolResult `json:"result"` // Execution result (contains Output)
|
||||
Reflection string `json:"reflection,omitempty"` // Agent's reflection on this tool call result (if enabled)
|
||||
Duration int64 `json:"duration"` // Execution time in milliseconds
|
||||
ProviderMetadata ToolCallMetadata `json:"provider_metadata,omitempty"` // Provider-specific tool-call state for replay
|
||||
}
|
||||
|
||||
// PipelineToolCallIDPrefix marks a persisted tool call the model never made.
|
||||
// The fast-answer (KnowledgeQA) pipeline records its retrieval stages as tool
|
||||
// calls so a reloaded conversation can redraw the same timeline it showed while
|
||||
// streaming. History replay must skip them: asking the model to account for
|
||||
// calls it never issued, against tools it may not even have, breaks the
|
||||
// request protocol.
|
||||
const PipelineToolCallIDPrefix = "ragpipe-"
|
||||
|
||||
// IsPipelineToolCallID reports whether a tool call was synthesized by the
|
||||
// fast-answer pipeline rather than requested by the model.
|
||||
func IsPipelineToolCallID(id string) bool {
|
||||
return strings.HasPrefix(id, PipelineToolCallIDPrefix)
|
||||
}
|
||||
|
||||
// AgentStep represents one iteration of the ReAct loop
|
||||
type AgentStep struct {
|
||||
Iteration int `json:"iteration"` // Iteration number (0-indexed)
|
||||
Thought string `json:"thought"` // LLM's reasoning/thinking (Think phase)
|
||||
// ReasoningContent stores the OpenAI-protocol reasoning_content emitted by the
|
||||
// model in this round. Persisted on AgentStep so cross-turn replay can put it
|
||||
// back on the assistant message — required by MiMo / DeepSeek V3.2+ thinking
|
||||
// mode, ignored by providers that don't recognize the field.
|
||||
ReasoningContent string `json:"reasoning_content,omitempty"`
|
||||
ToolCalls []ToolCall `json:"tool_calls"` // Tools called in this step (Act phase)
|
||||
Timestamp time.Time `json:"timestamp"` // When this step occurred
|
||||
}
|
||||
|
||||
// GetObservations returns observations from all tool calls in this step
|
||||
// This is a convenience method to maintain backward compatibility
|
||||
func (s *AgentStep) GetObservations() []string {
|
||||
observations := make([]string, 0, len(s.ToolCalls))
|
||||
for _, tc := range s.ToolCalls {
|
||||
if tc.Result != nil && tc.Result.Output != "" {
|
||||
observations = append(observations, tc.Result.Output)
|
||||
}
|
||||
if tc.Reflection != "" {
|
||||
observations = append(observations, "Reflection: "+tc.Reflection)
|
||||
}
|
||||
}
|
||||
return observations
|
||||
}
|
||||
|
||||
// AgentState tracks the execution state of an agent across iterations
|
||||
type AgentState struct {
|
||||
CurrentRound int `json:"current_round"` // Current round number
|
||||
RoundSteps []AgentStep `json:"round_steps"` // All steps taken so far in the current round
|
||||
IsComplete bool `json:"is_complete"` // Whether agent has finished
|
||||
FinalAnswer string `json:"final_answer"` // The final answer to the query
|
||||
KnowledgeRefs []*SearchResult `json:"knowledge_refs"` // Collected knowledge references
|
||||
TurnUsage TokenUsage `json:"turn_usage"` // LLM token usage accumulated across every round of this turn
|
||||
}
|
||||
|
||||
// FunctionDefinition represents a function definition for LLM function calling
|
||||
type FunctionDefinition struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Parameters json.RawMessage `json:"parameters"`
|
||||
}
|
||||
Reference in New Issue
Block a user