Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,6 @@ k8s-*/*
tests/**/creds.json
ui/simple-saas
*.tar
# AI tool config directories (user-specific)
.gemini/
.opencode/
7 changes: 7 additions & 0 deletions internal/agent/loop_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ type Loop struct {
skillEvolve bool
skillNudgeInterval int // nudge every N tool calls (0 = disabled, 15 = default)

// Strip trailing assistant messages before LLM call (for proxy providers)
stripAssistantPrefill bool

// Config permission store for group file writer checks
configPermStore store.ConfigPermissionStore

Expand Down Expand Up @@ -229,6 +232,9 @@ type LoopConfig struct {
// Thinking level: "off", "low", "medium", "high" (from agent other_config)
ThinkingLevel string

// Strip trailing assistant messages before LLM call (for proxy providers)
StripAssistantPrefill bool

// Self-evolve: predefined agents can update SOUL.md (style/tone) through chat
SelfEvolve bool

Expand Down Expand Up @@ -335,6 +341,7 @@ func NewLoop(cfg LoopConfig) *Loop {
selfEvolve: cfg.SelfEvolve,
skillEvolve: cfg.SkillEvolve,
skillNudgeInterval: cfg.SkillNudgeInterval,
stripAssistantPrefill: cfg.StripAssistantPrefill,
configPermStore: cfg.ConfigPermStore,
teamStore: cfg.TeamStore,
secureCLIStore: cfg.SecureCLIStore,
Expand Down
1 change: 1 addition & 0 deletions internal/agent/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ func NewManagedResolver(deps ResolverDeps) ResolverFunc {
SelfEvolve: ag.ParseSelfEvolve(),
SkillEvolve: ag.AgentType == store.AgentTypePredefined && ag.ParseSkillEvolve(),
SkillNudgeInterval: ag.ParseSkillNudgeInterval(),
StripAssistantPrefill: ag.ParseStripAssistantPrefill(),
WorkspaceSharing: ag.ParseWorkspaceSharing(),
ShellDenyGroups: ag.ParseShellDenyGroups(),
ConfigPermStore: deps.ConfigPermStore,
Expand Down
15 changes: 15 additions & 0 deletions internal/store/agent_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,21 @@ func (a *AgentData) ParseSkillNudgeInterval() int {
return *cfg.SkillNudgeInterval
}

// ParseStripAssistantPrefill extracts strip_assistant_prefill from other_config JSONB.
// When true, trailing assistant messages are removed before LLM call (for proxy providers).
func (a *AgentData) ParseStripAssistantPrefill() bool {
if len(a.OtherConfig) == 0 {
return false
}
var cfg struct {
StripAssistantPrefill bool `json:"strip_assistant_prefill"`
}
if json.Unmarshal(a.OtherConfig, &cfg) != nil {
return false
}
return cfg.StripAssistantPrefill
}

// WorkspaceSharingConfig controls per-user workspace isolation.
// When shared_dm/shared_group is true, users share the base workspace directory
// instead of each getting an isolated subfolder.
Expand Down
4 changes: 3 additions & 1 deletion ui/web/src/i18n/locales/en/agents.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@
"contextWindow": "Context Window",
"contextWindowHint": "Token limit for the model context.",
"maxToolIterations": "Max Tool Iterations",
"maxToolIterationsHint": "Max tool calls per request."
"maxToolIterationsHint": "Max tool calls per request.",
"stripAssistantPrefill": "Strip Assistant Prefill",
"stripAssistantPrefillHint": "Remove trailing assistant messages before LLM call. Enable for proxy providers that reject prefill."
},
"workspace": {
"title": "Workspace",
Expand Down
4 changes: 3 additions & 1 deletion ui/web/src/i18n/locales/vi/agents.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@
"contextWindow": "Cửa sổ ngữ cảnh",
"contextWindowHint": "Giới hạn token cho ngữ cảnh model.",
"maxToolIterations": "Số lần lặp công cụ tối đa",
"maxToolIterationsHint": "Số lần gọi công cụ tối đa mỗi yêu cầu."
"maxToolIterationsHint": "Số lần gọi công cụ tối đa mỗi yêu cầu.",
"stripAssistantPrefill": "Loại bỏ Assistant Prefill",
"stripAssistantPrefillHint": "Xóa tin nhắn assistant cuối cùng trước khi gọi LLM. Bật cho proxy provider không hỗ trợ prefill."
},
"workspace": {
"title": "Workspace",
Expand Down
4 changes: 3 additions & 1 deletion ui/web/src/i18n/locales/zh/agents.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@
"contextWindow": "上下文窗口",
"contextWindowHint": "模型上下文的令牌限制。",
"maxToolIterations": "最大工具迭代次数",
"maxToolIterationsHint": "每次请求最大工具调用次数。"
"maxToolIterationsHint": "每次请求最大工具调用次数。",
"stripAssistantPrefill": "移除助手预填充",
"stripAssistantPrefillHint": "在调用LLM前移除末尾的助手消息。为不支持预填充的代理提供商启用。"
},
"workspace": {
"title": "工作区",
Expand Down
5 changes: 5 additions & 0 deletions ui/web/src/pages/agents/agent-detail/agent-overview-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export function AgentOverviewTab({ agent, onUpdate, heartbeat }: AgentOverviewTa
const [budgetDollars, setBudgetDollars] = useState(
agent.budget_monthly_cents ? String(agent.budget_monthly_cents / 100) : "",
);
const [stripAssistantPrefill, setStripAssistantPrefill] = useState(Boolean(otherCfg.strip_assistant_prefill));

// Evolution (predefined only)
const [selfEvolve, setSelfEvolve] = useState(Boolean(otherCfg.self_evolve));
const [skillEvolve, setSkillEvolve] = useState(Boolean(otherCfg.skill_evolve));
Expand Down Expand Up @@ -70,6 +72,7 @@ export function AgentOverviewTab({ agent, onUpdate, heartbeat }: AgentOverviewTa
self_evolve: selfEvolve,
skill_evolve: skillEvolve,
skill_nudge_interval: skillEvolve ? skillNudgeInterval : undefined,
strip_assistant_prefill: stripAssistantPrefill || undefined,
};
const budgetCents = budgetDollars ? Math.round(parseFloat(budgetDollars) * 100) : null;
await onUpdate({
Expand Down Expand Up @@ -128,6 +131,8 @@ export function AgentOverviewTab({ agent, onUpdate, heartbeat }: AgentOverviewTa
budgetDollars={budgetDollars}
onBudgetDollarsChange={setBudgetDollars}
onSaveBlockedChange={setLlmSaveBlocked}
stripAssistantPrefill={stripAssistantPrefill}
onStripAssistantPrefillChange={setStripAssistantPrefill}
/>

<HeartbeatCard heartbeat={heartbeat} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next";
import { DollarSign } from "lucide-react";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import { ProviderModelSelect } from "@/components/shared/provider-model-select";

interface ModelBudgetSectionProps {
Expand All @@ -19,6 +20,8 @@ interface ModelBudgetSectionProps {
budgetDollars: string;
onBudgetDollarsChange: (v: string) => void;
onSaveBlockedChange?: (blocked: boolean) => void;
stripAssistantPrefill?: boolean;
onStripAssistantPrefillChange?: (v: boolean) => void;
}

export function ModelBudgetSection({
Expand All @@ -28,6 +31,8 @@ export function ModelBudgetSection({
savedProvider, savedModel,
budgetDollars, onBudgetDollarsChange,
onSaveBlockedChange,
stripAssistantPrefill,
onStripAssistantPrefillChange,
}: ModelBudgetSectionProps) {
const { t } = useTranslation("agents");

Expand Down Expand Up @@ -100,6 +105,20 @@ export function ModelBudgetSection({
</div>
<p className="text-xs text-muted-foreground">{t("general.budgetHint")}</p>
</div>

{onStripAssistantPrefillChange && (
<div className="flex items-center justify-between gap-4 rounded-md border px-3 py-2">
<div className="space-y-0.5">
<Label htmlFor="strip-prefill" className="text-xs">{t("llmConfig.stripAssistantPrefill")}</Label>
<p className="text-xs text-muted-foreground">{t("llmConfig.stripAssistantPrefillHint")}</p>
</div>
<Switch
id="strip-prefill"
checked={stripAssistantPrefill ?? false}
onCheckedChange={onStripAssistantPrefillChange}
/>
</div>
)}
</section>
);
}
Loading