From 0c15af90b1164d3df4a51b48b72b20f110d15dc2 Mon Sep 17 00:00:00 2001
From: Alex
Date: Mon, 6 Apr 2026 14:42:01 +0100
Subject: [PATCH 1/2] feat: history overwrite
---
.../api/answer/services/stream_processor.py | 144 ++++++++++-----
application/api/user/agents/routes.py | 30 ++++
application/api/v1/routes.py | 22 ++-
application/api/v1/translator.py | 22 ++-
docs/content/Agents/_meta.js | 4 +
docs/content/Agents/api.mdx | 4 +
docs/content/Agents/basics.mdx | 1 +
docs/content/Agents/openai-compatible.mdx | 93 ++++++++++
frontend/src/agents/NewAgent.tsx | 48 +++++
frontend/src/agents/types/index.ts | 1 +
.../src/agents/workflow/WorkflowBuilder.tsx | 42 +++++
frontend/src/locale/de.json | 4 +-
frontend/src/locale/en.json | 4 +-
frontend/src/locale/es.json | 4 +-
frontend/src/locale/jp.json | 4 +-
frontend/src/locale/ru.json | 4 +-
frontend/src/locale/zh-TW.json | 4 +-
frontend/src/locale/zh.json | 4 +-
.../answer/services/test_stream_processor.py | 164 ++++++++++++++++++
tests/api/answer/test_stream_processor.py | 26 +--
tests/test_v1_translator.py | 76 +++++++-
21 files changed, 635 insertions(+), 70 deletions(-)
create mode 100644 docs/content/Agents/openai-compatible.mdx
diff --git a/application/api/answer/services/stream_processor.py b/application/api/answer/services/stream_processor.py
index fe2d7d8c..79b6cc7b 100644
--- a/application/api/answer/services/stream_processor.py
+++ b/application/api/answer/services/stream_processor.py
@@ -112,6 +112,7 @@ class StreamProcessor:
self._required_tool_actions: Optional[Dict[str, Set[Optional[str]]]] = None
self.compressed_summary: Optional[str] = None
self.compressed_summary_tokens: int = 0
+ self._agent_data: Optional[Dict[str, Any]] = None
def initialize(self):
"""Initialize all required components for processing"""
@@ -359,22 +360,29 @@ class StreamProcessor:
return data
def _configure_source(self):
- """Configure the source based on agent data"""
- api_key = self.data.get("api_key") or self.agent_key
+ """Configure the source based on agent data.
- if api_key:
- agent_data = self._get_data_from_api_key(api_key)
+ The literal string ``"default"`` is a placeholder meaning "no
+ ingested source" and is normalized to an empty source so that no
+ retrieval is attempted.
+ """
+ if self._agent_data:
+ agent_data = self._agent_data
if agent_data.get("sources") and len(agent_data["sources"]) > 0:
source_ids = [
- source["id"] for source in agent_data["sources"] if source.get("id")
+ source["id"]
+ for source in agent_data["sources"]
+ if source.get("id") and source["id"] != "default"
]
if source_ids:
self.source = {"active_docs": source_ids}
else:
self.source = {}
- self.all_sources = agent_data["sources"]
- elif agent_data.get("source"):
+ self.all_sources = [
+ s for s in agent_data["sources"] if s.get("id") != "default"
+ ]
+ elif agent_data.get("source") and agent_data["source"] != "default":
self.source = {"active_docs": agent_data["source"]}
self.all_sources = [
{
@@ -387,11 +395,24 @@ class StreamProcessor:
self.all_sources = []
return
if "active_docs" in self.data:
- self.source = {"active_docs": self.data["active_docs"]}
+ active_docs = self.data["active_docs"]
+ if active_docs and active_docs != "default":
+ self.source = {"active_docs": active_docs}
+ else:
+ self.source = {}
return
self.source = {}
self.all_sources = []
+ def _has_active_docs(self) -> bool:
+ """Return True if a real document source is configured for retrieval."""
+ active_docs = self.source.get("active_docs") if self.source else None
+ if not active_docs:
+ return False
+ if active_docs == "default":
+ return False
+ return True
+
def _resolve_agent_id(self) -> Optional[str]:
"""Resolve agent_id from request, then fall back to conversation context."""
request_agent_id = self.data.get("agent_id")
@@ -433,48 +454,39 @@ class StreamProcessor:
effective_key = self.data.get("api_key") or self.agent_key
if effective_key:
- data_key = self._get_data_from_api_key(effective_key)
- if data_key.get("_id"):
- self.agent_id = str(data_key.get("_id"))
+ self._agent_data = self._get_data_from_api_key(effective_key)
+ if self._agent_data.get("_id"):
+ self.agent_id = str(self._agent_data.get("_id"))
self.agent_config.update(
{
- "prompt_id": data_key.get("prompt_id", "default"),
- "agent_type": data_key.get("agent_type", settings.AGENT_NAME),
+ "prompt_id": self._agent_data.get("prompt_id", "default"),
+ "agent_type": self._agent_data.get("agent_type", settings.AGENT_NAME),
"user_api_key": effective_key,
- "json_schema": data_key.get("json_schema"),
- "default_model_id": data_key.get("default_model_id", ""),
- "models": data_key.get("models", []),
+ "json_schema": self._agent_data.get("json_schema"),
+ "default_model_id": self._agent_data.get("default_model_id", ""),
+ "models": self._agent_data.get("models", []),
+ "allow_system_prompt_override": self._agent_data.get(
+ "allow_system_prompt_override", False
+ ),
}
)
# Set identity context
if self.data.get("api_key"):
# External API key: use the key owner's identity
- self.initial_user_id = data_key.get("user")
- self.decoded_token = {"sub": data_key.get("user")}
+ self.initial_user_id = self._agent_data.get("user")
+ self.decoded_token = {"sub": self._agent_data.get("user")}
elif self.is_shared_usage:
# Shared agent: keep the caller's identity
pass
else:
# Owner using their own agent
- self.decoded_token = {"sub": data_key.get("user")}
+ self.decoded_token = {"sub": self._agent_data.get("user")}
- if data_key.get("source"):
- self.source = {"active_docs": data_key["source"]}
- if data_key.get("workflow"):
- self.agent_config["workflow"] = data_key["workflow"]
- self.agent_config["workflow_owner"] = data_key.get("user")
- if data_key.get("retriever"):
- self.retriever_config["retriever_name"] = data_key["retriever"]
- if data_key.get("chunks") is not None:
- try:
- self.retriever_config["chunks"] = int(data_key["chunks"])
- except (ValueError, TypeError):
- logger.warning(
- f"Invalid chunks value: {data_key['chunks']}, using default value 2"
- )
- self.retriever_config["chunks"] = 2
+ if self._agent_data.get("workflow"):
+ self.agent_config["workflow"] = self._agent_data["workflow"]
+ self.agent_config["workflow_owner"] = self._agent_data.get("user")
else:
# No API key — default/workflow configuration
agent_type = settings.AGENT_NAME
@@ -497,14 +509,45 @@ class StreamProcessor:
)
def _configure_retriever(self):
+ """Assemble retriever config with precedence: request > agent > default."""
doc_token_limit = calculate_doc_token_budget(model_id=self.model_id)
+ # Start with defaults
+ retriever_name = "classic"
+ chunks = 2
+
+ # Layer agent-level config (if present)
+ if self._agent_data:
+ if self._agent_data.get("retriever"):
+ retriever_name = self._agent_data["retriever"]
+ if self._agent_data.get("chunks") is not None:
+ try:
+ chunks = int(self._agent_data["chunks"])
+ except (ValueError, TypeError):
+ logger.warning(
+ f"Invalid agent chunks value: {self._agent_data['chunks']}, "
+ "using default value 2"
+ )
+
+ # Explicit request values win over agent config
+ if "retriever" in self.data:
+ retriever_name = self.data["retriever"]
+ if "chunks" in self.data:
+ try:
+ chunks = int(self.data["chunks"])
+ except (ValueError, TypeError):
+ logger.warning(
+ f"Invalid request chunks value: {self.data['chunks']}, "
+ "using default value 2"
+ )
+
self.retriever_config = {
- "retriever_name": self.data.get("retriever", "classic"),
- "chunks": int(self.data.get("chunks", 2)),
+ "retriever_name": retriever_name,
+ "chunks": chunks,
"doc_token_limit": doc_token_limit,
}
+ # isNoneDoc without an API key forces no retrieval
api_key = self.data.get("api_key") or self.agent_key
if not api_key and "isNoneDoc" in self.data and self.data["isNoneDoc"]:
self.retriever_config["chunks"] = 0
@@ -528,6 +571,9 @@ class StreamProcessor:
if self.data.get("isNoneDoc", False) and not self.agent_id:
logger.info("Pre-fetch skipped: isNoneDoc=True")
return None, None
+ if not self._has_active_docs():
+ logger.info("Pre-fetch skipped: no active docs configured")
+ return None, None
try:
retriever = self.create_retriever()
logger.info(
@@ -910,15 +956,23 @@ class StreamProcessor:
raw_prompt = get_prompt(prompt_id, self.prompts_collection)
self._prompt_content = raw_prompt
- rendered_prompt = self.prompt_renderer.render_prompt(
- prompt_content=raw_prompt,
- user_id=self.initial_user_id,
- request_id=self.data.get("request_id"),
- passthrough_data=self.data.get("passthrough"),
- docs=docs,
- docs_together=docs_together,
- tools_data=tools_data,
- )
+ # Allow API callers to override the system prompt when the agent
+ # has opted in via allow_system_prompt_override.
+ if (
+ self.agent_config.get("allow_system_prompt_override", False)
+ and self.data.get("system_prompt_override")
+ ):
+ rendered_prompt = self.data["system_prompt_override"]
+ else:
+ rendered_prompt = self.prompt_renderer.render_prompt(
+ prompt_content=raw_prompt,
+ user_id=self.initial_user_id,
+ request_id=self.data.get("request_id"),
+ passthrough_data=self.data.get("passthrough"),
+ docs=docs,
+ docs_together=docs_together,
+ tools_data=tools_data,
+ )
provider = (
get_provider_from_model_id(self.model_id)
diff --git a/application/api/user/agents/routes.py b/application/api/user/agents/routes.py
index 296b8ea0..7d3dc93a 100644
--- a/application/api/user/agents/routes.py
+++ b/application/api/user/agents/routes.py
@@ -73,6 +73,7 @@ AGENT_TYPE_SCHEMAS = {
"token_limit",
"limited_request_mode",
"request_limit",
+ "allow_system_prompt_override",
"createdAt",
"updatedAt",
"lastUsedAt",
@@ -96,6 +97,7 @@ AGENT_TYPE_SCHEMAS = {
"token_limit",
"limited_request_mode",
"request_limit",
+ "allow_system_prompt_override",
"createdAt",
"updatedAt",
"lastUsedAt",
@@ -220,6 +222,12 @@ def build_agent_document(
base_doc["request_limit"] = int(
data.get("request_limit", settings.DEFAULT_AGENT_LIMITS["request_limit"])
)
+ if "allow_system_prompt_override" in allowed_fields:
+ base_doc["allow_system_prompt_override"] = (
+ data.get("allow_system_prompt_override") == "True"
+ if isinstance(data.get("allow_system_prompt_override"), str)
+ else bool(data.get("allow_system_prompt_override", False))
+ )
return {k: v for k, v in base_doc.items() if k in allowed_fields}
@@ -292,6 +300,9 @@ class GetAgent(Resource):
"default_model_id": agent.get("default_model_id", ""),
"folder_id": agent.get("folder_id"),
"workflow": agent.get("workflow"),
+ "allow_system_prompt_override": agent.get(
+ "allow_system_prompt_override", False
+ ),
}
return make_response(jsonify(data), 200)
except Exception as e:
@@ -373,6 +384,9 @@ class GetAgents(Resource):
"default_model_id": agent.get("default_model_id", ""),
"folder_id": agent.get("folder_id"),
"workflow": agent.get("workflow"),
+ "allow_system_prompt_override": agent.get(
+ "allow_system_prompt_override", False
+ ),
}
for agent in agents
if "source" in agent
@@ -450,6 +464,10 @@ class CreateAgent(Resource):
"folder_id": fields.String(
required=False, description="Folder ID to organize the agent"
),
+ "allow_system_prompt_override": fields.Boolean(
+ required=False,
+ description="Allow API callers to override the system prompt via the v1 endpoint",
+ ),
},
)
@@ -674,6 +692,10 @@ class UpdateAgent(Resource):
"folder_id": fields.String(
required=False, description="Folder ID to organize the agent"
),
+ "allow_system_prompt_override": fields.Boolean(
+ required=False,
+ description="Allow API callers to override the system prompt via the v1 endpoint",
+ ),
},
)
@@ -765,6 +787,7 @@ class UpdateAgent(Resource):
"default_model_id",
"folder_id",
"workflow",
+ "allow_system_prompt_override",
]
for field in allowed_fields:
@@ -983,6 +1006,13 @@ class UpdateAgent(Resource):
if workflow_error:
return workflow_error
update_fields[field] = workflow_id
+ elif field == "allow_system_prompt_override":
+ raw_value = data.get("allow_system_prompt_override", False)
+ update_fields[field] = (
+ raw_value == "True"
+ if isinstance(raw_value, str)
+ else bool(raw_value)
+ )
else:
value = data[field]
if field in ["name", "description", "prompt_id", "agent_type"]:
diff --git a/application/api/v1/routes.py b/application/api/v1/routes.py
index 4d5a513b..8b1be646 100644
--- a/application/api/v1/routes.py
+++ b/application/api/v1/routes.py
@@ -138,10 +138,18 @@ def chat_completions():
if usage_error:
return usage_error
+ should_save_conversation = bool(internal_data.get("save_conversation", False))
+
if is_stream:
return Response(
_stream_response(
- helper, question, agent, processor, model_name, continuation
+ helper,
+ question,
+ agent,
+ processor,
+ model_name,
+ continuation,
+ should_save_conversation,
),
mimetype="text/event-stream",
headers={
@@ -151,7 +159,13 @@ def chat_completions():
)
else:
return _non_stream_response(
- helper, question, agent, processor, model_name, continuation
+ helper,
+ question,
+ agent,
+ processor,
+ model_name,
+ continuation,
+ should_save_conversation,
)
except ValueError as e:
@@ -181,6 +195,7 @@ def _stream_response(
processor: StreamProcessor,
model_name: str,
continuation: Optional[Dict],
+ should_save_conversation: bool,
) -> Generator[str, None, None]:
"""Generate translated SSE chunks for streaming response."""
completion_id = f"chatcmpl-{int(time.time())}"
@@ -193,6 +208,7 @@ def _stream_response(
decoded_token=processor.decoded_token,
agent_id=processor.agent_id,
model_id=processor.model_id,
+ should_save_conversation=should_save_conversation,
_continuation=continuation,
)
@@ -225,6 +241,7 @@ def _non_stream_response(
processor: StreamProcessor,
model_name: str,
continuation: Optional[Dict],
+ should_save_conversation: bool,
) -> Response:
"""Collect full response and return as single JSON."""
stream = helper.complete_stream(
@@ -235,6 +252,7 @@ def _non_stream_response(
decoded_token=processor.decoded_token,
agent_id=processor.agent_id,
model_id=processor.model_id,
+ should_save_conversation=should_save_conversation,
_continuation=continuation,
)
diff --git a/application/api/v1/translator.py b/application/api/v1/translator.py
index 3d3c64fe..5330a029 100644
--- a/application/api/v1/translator.py
+++ b/application/api/v1/translator.py
@@ -80,6 +80,17 @@ def extract_conversation_id(messages: List[Dict]) -> Optional[str]:
return None
+def extract_system_prompt(messages: List[Dict]) -> Optional[str]:
+ """Extract the first system message content from the messages array.
+
+ Returns None if no system message is present.
+ """
+ for msg in messages:
+ if msg.get("role") == "system":
+ return msg.get("content", "")
+ return None
+
+
def convert_history(messages: List[Dict]) -> List[Dict]:
"""Convert chat completions messages array to DocsGPT history format.
@@ -148,20 +159,27 @@ def translate_request(
break
history = convert_history(messages)
+ system_prompt_override = extract_system_prompt(messages)
+
+ docsgpt = data.get("docsgpt", {})
result = {
"question": question,
"api_key": api_key,
"history": json.dumps(history),
- "save_conversation": True,
+ # Conversations are NOT persisted by default on the v1 endpoint.
+ # Callers opt in via ``docsgpt.save_conversation: true``.
+ "save_conversation": bool(docsgpt.get("save_conversation", False)),
}
+ if system_prompt_override is not None:
+ result["system_prompt_override"] = system_prompt_override
+
# Client tools
if data.get("tools"):
result["client_tools"] = data["tools"]
# DocsGPT extensions
- docsgpt = data.get("docsgpt", {})
if docsgpt.get("attachments"):
result["attachments"] = docsgpt["attachments"]
diff --git a/docs/content/Agents/_meta.js b/docs/content/Agents/_meta.js
index c38c6e36..61900068 100644
--- a/docs/content/Agents/_meta.js
+++ b/docs/content/Agents/_meta.js
@@ -7,6 +7,10 @@ export default {
"title": "🔌 Agent API",
"href": "/Agents/api"
},
+ "openai-compatible": {
+ "title": "🔄 OpenAI-Compatible API",
+ "href": "/Agents/openai-compatible"
+ },
"webhooks": {
"title": "🪝 Agent Webhooks",
"href": "/Agents/webhooks"
diff --git a/docs/content/Agents/api.mdx b/docs/content/Agents/api.mdx
index 5b1313dd..ad77a69a 100644
--- a/docs/content/Agents/api.mdx
+++ b/docs/content/Agents/api.mdx
@@ -15,6 +15,10 @@ DocsGPT Agents can be accessed programmatically through API endpoints. This page
When you use an agent `api_key`, DocsGPT loads that agent's configuration automatically (prompt, tools, sources, default model). You usually only need to send `question` and `api_key`.
+
+Looking to connect an existing OpenAI-compatible client (opencode, aider, the OpenAI SDKs, etc.) to a DocsGPT Agent? Use the [OpenAI-Compatible Chat Completions API](/Agents/openai-compatible) — it speaks the standard chat completions protocol so no adapter code is required.
+
+
## Base URL
diff --git a/docs/content/Agents/basics.mdx b/docs/content/Agents/basics.mdx
index 3ad407df..741df77b 100644
--- a/docs/content/Agents/basics.mdx
+++ b/docs/content/Agents/basics.mdx
@@ -111,6 +111,7 @@ Once an agent is created, you can:
* Modify any of its configuration settings (name, description, source, prompt, tools, type).
* **Generate a Public Link:** From the edit screen, you can create a shareable public link that allows others to import and use your agent.
* **Get a Webhook URL:** You can also obtain a Webhook URL for the agent. This allows external applications or services to trigger the agent and receive responses programmatically, enabling powerful integrations and automations.
+* **Use it via API:** Every agent exposes an API key that can be used with the native [Agent API](/Agents/api) or the [OpenAI-Compatible API](/Agents/openai-compatible) so you can drop DocsGPT Agents into any tool that already speaks the chat completions protocol.
## Seeding Premade Agents from YAML
diff --git a/docs/content/Agents/openai-compatible.mdx b/docs/content/Agents/openai-compatible.mdx
new file mode 100644
index 00000000..0994ec59
--- /dev/null
+++ b/docs/content/Agents/openai-compatible.mdx
@@ -0,0 +1,93 @@
+---
+title: OpenAI-Compatible API
+description: Connect any OpenAI-compatible client to DocsGPT Agents via /v1/chat/completions.
+---
+
+import { Callout, Tabs } from 'nextra/components';
+
+# OpenAI-Compatible API
+
+DocsGPT exposes `/v1/chat/completions` following the standard chat completions protocol. Point any compatible client — **opencode**, **Aider**, **LibreChat** or the OpenAI SDKs — at your DocsGPT Agent by changing only the base URL and API key.
+
+## Quick Start
+
+
+
+ ```python
+ from openai import OpenAI
+
+ client = OpenAI(
+ base_url="http://localhost:7091/v1", # or https://gptcloud.arc53.com/v1
+ api_key="your_agent_api_key",
+ )
+
+ response = client.chat.completions.create(
+ model="docsgpt-agent",
+ messages=[{"role": "user", "content": "Summarize our refund policy"}],
+ )
+ print(response.choices[0].message.content)
+ ```
+
+
+ ```bash
+ curl -X POST http://localhost:7091/v1/chat/completions \
+ -H "Authorization: Bearer your_agent_api_key" \
+ -H "Content-Type: application/json" \
+ -d '{"model":"docsgpt-agent","messages":[{"role":"user","content":"Summarize our refund policy"}]}'
+ ```
+
+
+
+The `model` field is accepted but ignored — the agent bound to your API key determines the model. The agent's prompt, sources, tools, and default model are loaded automatically.
+
+## Base URL & Auth
+
+| Environment | Base URL |
+| --- | --- |
+| Local | `http://localhost:7091/v1` |
+| Cloud | `https://gptcloud.arc53.com/v1` |
+
+Authenticate with `Authorization: Bearer `.
+
+## Endpoints
+
+| Method | Path | Description |
+| --- | --- | --- |
+| `POST` | `/v1/chat/completions` | Chat request (streaming or non-streaming) |
+| `GET` | `/v1/models` | List agents available to your key |
+
+## Streaming
+
+Set `"stream": true`. You'll receive SSE chunks with `choices[0].delta.content`. DocsGPT-specific events (sources, tool calls) arrive as extra frames with a `docsgpt` key — standard clients ignore them.
+
+```python
+stream = client.chat.completions.create(
+ model="docsgpt-agent",
+ stream=True,
+ messages=[{"role": "user", "content": "Explain vector search"}],
+)
+for chunk in stream:
+ print(chunk.choices[0].delta.content or "", end="", flush=True)
+```
+
+## System Prompt Override
+
+System messages are **dropped by default** — the agent's configured prompt is used. To allow callers to override it, enable **Allow prompt override** in the agent's Advanced settings.
+
+
+When an override is active, the agent's prompt template is replaced wholesale — template variables like `{summaries}` are not substituted.
+
+
+## Conversation Persistence
+
+Conversations are **not persisted by default** (stateless, like most OpenAI clients expect). Opt in per request:
+
+```json
+{ "docsgpt": { "save_conversation": true } }
+```
+
+The response will include `docsgpt.conversation_id`.
+
+## When to Use Native Endpoints Instead
+
+Use [`/api/answer` or `/stream`](/Agents/api) if you need server-side attachments, `passthrough` template variables, explicit `conversation_id` reuse, or persistence by default.
diff --git a/frontend/src/agents/NewAgent.tsx b/frontend/src/agents/NewAgent.tsx
index 133ffc7c..938b2e4f 100644
--- a/frontend/src/agents/NewAgent.tsx
+++ b/frontend/src/agents/NewAgent.tsx
@@ -73,6 +73,7 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
token_limit: undefined,
limited_request_mode: false,
request_limit: undefined,
+ allow_system_prompt_override: false,
models: [],
default_model_id: '',
});
@@ -241,6 +242,11 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
formData.append('request_limit', '0');
}
+ formData.append(
+ 'allow_system_prompt_override',
+ agent.allow_system_prompt_override ? 'True' : 'False',
+ );
+
if (imageFile) formData.append('image', imageFile);
if (agent.tools && agent.tools.length > 0)
@@ -361,6 +367,11 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
formData.append('request_limit', '0');
}
+ formData.append(
+ 'allow_system_prompt_override',
+ agent.allow_system_prompt_override ? 'True' : 'False',
+ );
+
if (agent.models && agent.models.length > 0) {
formData.append('models', JSON.stringify(agent.models));
}
@@ -1266,6 +1277,43 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
}`}
/>
+
+
+
+
+
+ {t('agents.form.advanced.systemPromptOverride')}
+
+
+ {t(
+ 'agents.form.advanced.systemPromptOverrideDescription',
+ )}
+
+
+
+
+
)}
diff --git a/frontend/src/agents/types/index.ts b/frontend/src/agents/types/index.ts
index 6e732401..12e27c8a 100644
--- a/frontend/src/agents/types/index.ts
+++ b/frontend/src/agents/types/index.ts
@@ -36,6 +36,7 @@ export type Agent = {
default_model_id?: string;
folder_id?: string;
workflow?: string;
+ allow_system_prompt_override?: boolean;
};
export type AgentFolder = {
diff --git a/frontend/src/agents/workflow/WorkflowBuilder.tsx b/frontend/src/agents/workflow/WorkflowBuilder.tsx
index 2024ee7c..abdddf46 100644
--- a/frontend/src/agents/workflow/WorkflowBuilder.tsx
+++ b/frontend/src/agents/workflow/WorkflowBuilder.tsx
@@ -1142,6 +1142,10 @@ function WorkflowBuilderInner() {
workflowDescription || `Workflow agent: ${workflowName}`,
);
agentFormData.append('status', 'published');
+ agentFormData.append(
+ 'allow_system_prompt_override',
+ currentAgent.allow_system_prompt_override ? 'True' : 'False',
+ );
if (imageFile) {
agentFormData.append('image', imageFile);
}
@@ -1203,6 +1207,10 @@ function WorkflowBuilderInner() {
agentFormData.append('agent_type', 'workflow');
agentFormData.append('status', 'published');
agentFormData.append('workflow', savedWorkflowId || '');
+ agentFormData.append(
+ 'allow_system_prompt_override',
+ currentAgent.allow_system_prompt_override ? 'True' : 'False',
+ );
if (imageFile) {
agentFormData.append('image', imageFile);
}
@@ -1454,6 +1462,40 @@ function WorkflowBuilderInner() {
Image updates are included the next time you save.
+
+
+
+
+
+ Let v1 API callers replace the system prompt
+
+
+
+
+