-
Notifications
You must be signed in to change notification settings - Fork 5
fix: use graph_config as source of truth in marketplace #185
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Backend: Store graph_config in agent snapshots for full config preservation - Backend: Restore graph_config when forking agents and pulling updates - Backend: Add graph_config support to updateAgentAndPublish endpoint - Frontend: Add graph_config to AgentSnapshot and UpdateAgentRequest types - Frontend: Update config display to read from graph_config with legacy fallback - Frontend: Add Configuration tab with Visual/JSON editor in AgentMarketplaceManage - Frontend: Reuse existing AgentGraphEditor and JsonEditor components - Add translations for config editor UI elements (en/zh/ja) Co-Authored-By: Claude <[email protected]>
审阅者指南后端现在将 通过 marketplace 管理页 Configuration 标签更新 agent
|
| Change | Details | Files |
|---|---|---|
在 marketplace 快照中持久化 graph_config,并在 fork 或 pull agent 时予以传播,从而使其成为配置的单一事实来源。 |
|
service/app/core/marketplace/agent_marketplace_service.py |
通过 marketplace API 和 TypeScript 类型在更新流程中暴露 graph_config。 |
|
service/app/api/v1/marketplace.pyservice/app/core/marketplace/agent_marketplace_service.pyweb/src/service/marketplaceService.ts |
| 在前后端统一从配置中派生可展示的系统提示词。 |
|
service/app/core/prompts/defaults.pyweb/src/app/marketplace/AgentMarketplaceDetail.tsx |
使用 graph_config 元数据在 marketplace 详情页展示 agent 类型和配置信息。 |
|
web/src/app/marketplace/AgentMarketplaceDetail.tsxweb/src/service/marketplaceService.ts |
在 marketplace 管理页面增加 Configuration 标签页,提供 graph_config 的可视化和 JSON 编辑器,并接入更新并发布流程。 |
|
web/src/app/marketplace/AgentMarketplaceManage.tsx |
| 为新的配置编辑 UI 文案在所有支持语言中做本地化。 |
|
web/src/i18n/locales/en/marketplace.jsonweb/src/i18n/locales/ja/marketplace.jsonweb/src/i18n/locales/zh/marketplace.jsonweb/src/app/marketplace/AgentMarketplaceManage.tsx |
技巧与命令
与 Sourcery 交互
- 触发新一次审查: 在 pull request 上评论
@sourcery-ai review。 - 继续讨论: 直接回复 Sourcery 的审查评论。
- 从审查评论生成 GitHub issue: 在某条审查评论下回复,请求 Sourcery 基于该评论创建 issue。也可以直接在该评论下回复
@sourcery-ai issue来生成 issue。 - 生成 pull request 标题: 在 pull request 标题中任意位置写上
@sourcery-ai即可随时生成标题。也可以在 pull request 中评论@sourcery-ai title来(重新)生成标题。 - 生成 pull request 摘要: 在 pull request 内容中任意位置写上
@sourcery-ai summary,即可在对应位置生成 PR 摘要。也可以在 pull request 中评论@sourcery-ai summary来随时(重新)生成摘要。 - 生成审阅者指南: 在 pull request 中评论
@sourcery-ai guide来随时(重新)生成审阅者指南。 - 解决所有 Sourcery 评论: 在 pull request 中评论
@sourcery-ai resolve,将所有 Sourcery 评论标记为已解决。如果你已经处理完所有评论且不想再看到它们,这会很有用。 - 撤销所有 Sourcery 审查: 在 pull request 中评论
@sourcery-ai dismiss来撤销所有已有的 Sourcery 审查。如果你想从一次全新的审查开始,这尤其有用——别忘了重新评论@sourcery-ai review以触发新审查!
自定义你的体验
访问你的 控制面板 以:
- 启用或禁用诸如 Sourcery 生成的 pull request 摘要、审阅者指南等审查功能。
- 更改审查语言。
- 添加、移除或编辑自定义审查指令。
- 调整其他审查设置。
获取帮助
Original review guide in English
Reviewer's Guide
Backend now treats agent.graph_config as the canonical configuration, persisting it in marketplace snapshots, restoring it on fork/pull, and allowing updates via an extended UpdateAgentRequest; frontend surfaces this by wiring graph_config through marketplaceService, showing agent type and prompt derived from graph_config, and adding a marketplace management Configuration tab with visual/JSON editors backed by new i18n strings.
Sequence diagram for updating agent graph_config via marketplace manage Configuration tab
sequenceDiagram
actor Manager
participant ManagePage as AgentMarketplaceManage
participant VisualEditor as AgentGraphEditor
participant JsonEditor
participant MarketplaceService as marketplaceService
participant API as MarketplaceAPI_v1_update_agent_and_listing
participant Service as AgentMarketplaceService
participant Repo as AgentRepository
participant SnapshotStore as MarketplaceSnapshots
Manager->>ManagePage: Open manage page for listing
ManagePage->>MarketplaceService: getListing(marketplaceId)
MarketplaceService->>API: GET /marketplace/{id}
API-->>MarketplaceService: listing with snapshot.configuration.graph_config
MarketplaceService-->>ManagePage: listing data
ManagePage->>ManagePage: set graphConfig state from snapshot / default
Manager->>ManagePage: Switch to Configuration tab
Manager->>ManagePage: Click Edit configuration
ManagePage->>VisualEditor: Render with value graphConfig
Manager->>VisualEditor: Modify graph visually
VisualEditor-->>ManagePage: onChange(config)
ManagePage->>ManagePage: handleGraphConfigChange(config)
ManagePage->>JsonEditor: sync JSON view with graphConfigJson
Manager->>JsonEditor: Edit JSON config
JsonEditor-->>ManagePage: onChange(jsonValue)
ManagePage->>ManagePage: handleJsonChange(value)
ManagePage->>ManagePage: handleJsonValidation(isValid, errors)
Manager->>ManagePage: Click Save configuration
ManagePage->>ManagePage: saveConfig()
ManagePage->>MarketplaceService: updateAgentAndPublish(listing.id, {commit_message, graph_config})
MarketplaceService->>API: POST /marketplace/{id}/update {commit_message, graph_config}
API->>Service: update_agent_and_listing(request)
Service->>Service: update_agent_and_publish(marketplace_id, agent_update, commit_message, graph_config)
Service->>Repo: update_agent(agent.id, {graph_config, other_fields})
Repo-->>Service: updated Agent
Service->>SnapshotStore: create_snapshot_from_agent(agent, commit_message)
SnapshotStore-->>Service: stored snapshot
Service-->>API: updated listing with new snapshot
API-->>MarketplaceService: response
MarketplaceService-->>ManagePage: updated listing
ManagePage->>ManagePage: reset editing state
ManagePage->>MarketplaceService: invalidate listing and history queries
MarketplaceService->>API: refetch listing and history
API-->>MarketplaceService: latest data
MarketplaceService-->>ManagePage: updated listing and history
ManagePage-->>Manager: Show new configuration snapshot
Class diagram for graph_config as source of truth across marketplace backend and frontend
classDiagram
class Agent {
UUID id
string name
string description
list~string~ tags
string model
float temperature
string prompt
bool require_tool_confirmation
string scope
dict graph_config
}
class AgentSnapshotConfiguration {
list~string~ tags
string model
float temperature
string prompt
bool require_tool_confirmation
string scope
dict graph_config
}
class AgentSnapshot {
string id
int version
string commit_message
AgentSnapshotConfiguration configuration
}
class AgentMarketplaceUpdate {
string name
string description
string avatar
list~string~ tags
}
class UpdateAgentRequest {
string name
string description
string avatar
list~string~ tags
string readme
string commit_message
dict graph_config
}
class AgentMarketplaceService {
+create_snapshot_from_agent(agent Agent, commit_message string) AgentSnapshot
+fork_agent(marketplace_id UUID, user_id string, fork_name string) Agent
+update_agent_and_publish(marketplace_id UUID, agent_update AgentMarketplaceUpdate, commit_message string, graph_config dict) AgentMarketplace
+pull_listing_update(agent_id UUID, user_id string) Agent
}
class AgentRepository {
+update_agent(agent_id UUID, update_data dict) Agent
}
class defaults_module {
+get_display_prompt_from_config(config dict) string
}
class marketplaceService_ts {
<<TypeScript>>
+AgentSnapshot
+UpdateAgentRequest
+RequirementsResponse
}
AgentSnapshotConfiguration <|-- AgentSnapshot
Agent "1" <-- "many" AgentSnapshot : snapshots
AgentMarketplaceService --> Agent : reads_writes
AgentMarketplaceService --> AgentSnapshot : creates
AgentMarketplaceService --> AgentRepository : uses
UpdateAgentRequest --> AgentMarketplaceService : passed_to_update_agent_and_publish
AgentSnapshotConfiguration --> defaults_module : used_by_get_display_prompt_from_config
marketplaceService_ts --> UpdateAgentRequest : mirrors_schema
marketplaceService_ts --> AgentSnapshot : mirrors_schema
File-Level Changes
| Change | Details | Files |
|---|---|---|
| Persist graph_config in marketplace snapshots and propagate it when forking or pulling agents so it becomes the configuration source of truth. |
|
service/app/core/marketplace/agent_marketplace_service.py |
| Expose graph_config through the marketplace API and TypeScript types for update flows. |
|
service/app/api/v1/marketplace.pyservice/app/core/marketplace/agent_marketplace_service.pyweb/src/service/marketplaceService.ts |
| Standardize how a displayable system prompt is derived from configuration in both backend and frontend. |
|
service/app/core/prompts/defaults.pyweb/src/app/marketplace/AgentMarketplaceDetail.tsx |
| Surface agent type and configuration details on the marketplace detail page using graph_config metadata. |
|
web/src/app/marketplace/AgentMarketplaceDetail.tsxweb/src/service/marketplaceService.ts |
| Add a Configuration tab to the marketplace management page with both visual and JSON editors for graph_config, wired to the update-and-publish flow. |
|
web/src/app/marketplace/AgentMarketplaceManage.tsx |
| Localize the new configuration editing UI strings across supported languages. |
|
web/src/i18n/locales/en/marketplace.jsonweb/src/i18n/locales/ja/marketplace.jsonweb/src/i18n/locales/zh/marketplace.jsonweb/src/app/marketplace/AgentMarketplaceManage.tsx |
Tips and commands
Interacting with Sourcery
- Trigger a new review: Comment
@sourcery-ai reviewon the pull request. - Continue discussions: Reply directly to Sourcery's review comments.
- Generate a GitHub issue from a review comment: Ask Sourcery to create an
issue from a review comment by replying to it. You can also reply to a
review comment with@sourcery-ai issueto create an issue from it. - Generate a pull request title: Write
@sourcery-aianywhere in the pull
request title to generate a title at any time. You can also comment
@sourcery-ai titleon the pull request to (re-)generate the title at any time. - Generate a pull request summary: Write
@sourcery-ai summaryanywhere in
the pull request body to generate a PR summary at any time exactly where you
want it. You can also comment@sourcery-ai summaryon the pull request to
(re-)generate the summary at any time. - Generate reviewer's guide: Comment
@sourcery-ai guideon the pull
request to (re-)generate the reviewer's guide at any time. - Resolve all Sourcery comments: Comment
@sourcery-ai resolveon the
pull request to resolve all Sourcery comments. Useful if you've already
addressed all the comments and don't want to see them anymore. - Dismiss all Sourcery reviews: Comment
@sourcery-ai dismisson the pull
request to dismiss all existing Sourcery reviews. Especially useful if you
want to start fresh with a new review - don't forget to comment
@sourcery-ai reviewto trigger a new review!
Customizing Your Experience
Access your dashboard to:
- Enable or disable review features such as the Sourcery-generated pull request
summary, the reviewer's guide, and others. - Change the review language.
- Add, remove or edit custom review instructions.
- Adjust other review settings.
Getting Help
- Contact our support team for questions or feedback.
- Visit our documentation for detailed guides and information.
- Keep in touch with the Sourcery team by following us on X/Twitter, LinkedIn or GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey - 我发现了两个问题,并留下了一些更高层次的反馈:
- 在
AgentMarketplaceManage中构造的默认 ReActgraph_config是硬编码的,而且注释中说明它必须与后端的REACT_CONFIG保持一致;建议将其提取为一个共享常量,或者从后端派生出来,以避免未来产生偏差。 - 当前后端(
get_display_prompt_from_config)和前端(getDisplayPrompt)都包含了从graph_config中提取展示提示(display prompt)的逻辑;为了更稳健,可以考虑让后端暴露一个单一的派生字段display_prompt,这样逻辑就不会重复,并且可以在一个地方演进。 - 类似地,
getAgentType在客户端是通过graph_config来推断 agent 类型的;你可以考虑在 API 中提供一个显式的agent_type或类似的派生字段,而不是依赖对原始配置结构的启发式判断。
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The default ReAct `graph_config` constructed in `AgentMarketplaceManage` is hard-coded and comments say it must match the backend's `REACT_CONFIG`; consider moving this to a shared constant or deriving it from the backend to avoid future drift.
- Both the backend (`get_display_prompt_from_config`) and frontend (`getDisplayPrompt`) now embed logic for extracting a display prompt from `graph_config`; it may be more robust to have the backend expose a single derived `display_prompt` field so this logic isn't duplicated and can evolve in one place.
- Similarly, `getAgentType` infers agent type from `graph_config` on the client; you might consider surfacing an explicit `agent_type` or similar derived field from the API instead of relying on heuristics over the raw config structure.
## Individual Comments
### Comment 1
<location> `service/app/core/prompts/defaults.py:91-93` </location>
<code_context>
+ The display prompt string or None if not found
+ """
+ # Priority 1: Check graph_config.prompt_config.custom_instructions
+ graph_config = config.get("graph_config")
+ if graph_config:
+ prompt_config = graph_config.get("prompt_config", {})
+ if custom_instructions := prompt_config.get("custom_instructions"):
+ return custom_instructions
</code_context>
<issue_to_address>
**issue:** `get_display_prompt_from_config` 假设 `graph_config` 是一个字典,这可能在运行时引发异常。
如果 `config["graph_config"]` 不是字典(例如 `None`、列表、JSON 字符串),`graph_config.get(...)` 会抛出 `AttributeError`。建议在调用 `.get` 之前,先使用 `isinstance(graph_config, dict)` 做防护,因为存储的快照结构未来可能会演变。
</issue_to_address>
### Comment 2
<location> `web/src/app/marketplace/AgentMarketplaceManage.tsx:62` </location>
<code_context>
+ "editor",
+ );
+
+ // Configuration editing state
+ const [isEditingConfig, setIsEditingConfig] = useState(false);
+ const [graphConfig, setGraphConfig] = useState<GraphConfig | null>(null);
</code_context>
<issue_to_address>
**issue (complexity):** 建议将新的配置编辑器抽取到一个独立的 `ConfigEditor` 组件中,使用单一规范的 JSON 状态和更清晰的 Tab 处理,以保持 `AgentMarketplaceManage` 更加简洁聚焦。
你可以保留现在的新功能,但通过抽取并简化配置编辑器逻辑,让主组件更容易理解。
### 1. 抽取 `ConfigEditor` 子组件
大部分新增的 state 和处理函数都是与配置相关的,可以放在一个专门的组件里。这可以从 `AgentMarketplaceManage` 中移除大量噪音。
**在简化后的 `AgentMarketplaceManage` 中:**
```tsx
// keep only high-level state here
const [activeTab, setActiveTab] = useState<"editor" | "config" | "history">("editor");
// ...
{activeTab === "config" && listing && (
<ConfigEditor
listingId={listing.id}
initialGraphConfig={listing.snapshot?.configuration?.graph_config ?? null}
/>
)}
```
### 2. 将配置 state + 逻辑移入 `ConfigEditor`
封装以下内容:
- `isEditingConfig`
- `graphConfig` / `graphConfigJson` / `graphConfigError`
- `activeEditorTab`
- `isSavingConfig`
- `startEditingConfig`, `cancelEditingConfig`, `saveConfig`
- JSON ↔ config 的同步
示例骨架:
```tsx
// ConfigEditor.tsx
import { useCallback, useState } from "react";
import { AgentGraphEditor } from "@/components/editors/AgentGraphEditor";
import { JsonEditor } from "@/components/editors/JsonEditor";
import type { GraphConfig } from "@/types/graphConfig";
import { marketplaceService } from "@/service/marketplaceService";
import { useQueryClient } from "@tanstack/react-query";
import { defaultReactGraphConfig } from "@/config/defaultReactGraphConfig";
interface ConfigEditorProps {
listingId: string;
initialGraphConfig: GraphConfig | Record<string, unknown> | null;
}
export function ConfigEditor({ listingId, initialGraphConfig }: ConfigEditorProps) {
const queryClient = useQueryClient();
const [isEditingConfig, setIsEditingConfig] = useState(false);
const [activeEditorTab, setActiveEditorTab] = useState<"visual" | "json">("visual");
const [graphConfigJson, setGraphConfigJson] = useState(
initialGraphConfig ? JSON.stringify(initialGraphConfig, null, 2) : ""
);
const [graphConfigError, setGraphConfigError] = useState<string | null>(null);
const [isSavingConfig, setIsSavingConfig] = useState(false);
// single source of truth: JSON
const handleJsonChange = useCallback((value: string) => {
setGraphConfigJson(value);
setGraphConfigError(null); // let validation handle errors
}, []);
const handleJsonValidation = useCallback((isValid: boolean, errors: string[]) => {
setGraphConfigError(isValid ? null : errors[0] ?? "Invalid JSON");
}, []);
const handleGraphConfigChange = useCallback((config: GraphConfig) => {
setGraphConfigJson(JSON.stringify(config, null, 2));
setGraphConfigError(null);
}, []);
const startEditingConfig = () => {
if (!graphConfigJson.trim()) {
setGraphConfigJson(JSON.stringify(defaultReactGraphConfig, null, 2));
}
setGraphConfigError(null);
setActiveEditorTab("visual");
setIsEditingConfig(true);
};
const cancelEditingConfig = () => {
setIsEditingConfig(false);
setGraphConfigError(null);
// keep graphConfigJson so user can resume later if desired
};
const saveConfig = async () => {
if (graphConfigError) return;
let finalGraphConfig: Record<string, unknown> | null = null;
if (graphConfigJson.trim()) {
try {
finalGraphConfig = JSON.parse(graphConfigJson);
} catch {
setGraphConfigError("Invalid JSON");
return;
}
}
try {
setIsSavingConfig(true);
await marketplaceService.updateAgentAndPublish(listingId, {
commit_message: "Updated agent configuration",
graph_config: finalGraphConfig,
});
await Promise.all([
queryClient.invalidateQueries({ queryKey: ["marketplace", "listing", listingId] }),
queryClient.invalidateQueries({ queryKey: ["marketplace", "history", listingId] }),
]);
setIsEditingConfig(false);
} finally {
setIsSavingConfig(false);
}
};
// render visual/json tabs using activeEditorTab string, but with simple tab switch:
// setActiveEditorTab("visual" | "json") – no extra logic
return (
// use the same JSX structure you already have, but driven by the local state above
);
}
```
### 3. 使用单一的规范配置状态
目前你同时维护了 `graphConfig` 和 `graphConfigJson`,并在多个地方进行 parse/stringify。更简单的模式是:
- 规范状态:`graphConfigJson`(字符串)
- 只有在可视化编辑器需要时才派生 `GraphConfig`(在子组件内部,而不是顶层)
在 `ConfigEditor` 内部的示例:
```tsx
// derive parsed config for visual editor only
const parsedGraphConfig = useMemo<GraphConfig | null>(() => {
if (!graphConfigJson.trim() || graphConfigError) return null;
try {
return JSON.parse(graphConfigJson) as GraphConfig;
} catch {
return null;
}
}, [graphConfigJson, graphConfigError]);
// pass to AgentGraphEditor
<AgentGraphEditor
value={parsedGraphConfig}
onChange={handleGraphConfigChange}
height="100%"
graphId={listingId}
/>
```
这样就不需要在 `handleEditorTabChange`、`saveConfig` 和 `startEditingConfig` 中重复进行 parse 了。
### 4. 用语义化标识替换数字 Tab 索引
相比使用 `activeEditorTab: number` 和 `if (activeEditorTab === 1 && index === 0 ...)`,可以使用语义化的标识,并让 Tab 切换成为一个纯粹的状态变更:
```tsx
const [activeEditorTab, setActiveEditorTab] = useState<"visual" | "json">("visual");
// with Headless UI TabGroup
<TabGroup
selectedIndex={activeEditorTab === "visual" ? 0 : 1}
onChange={(index) => setActiveEditorTab(index === 0 ? "visual" : "json")}
>
{/* ... */}
</TabGroup>
```
没有隐式转换逻辑;同步由规范状态 `graphConfigJson` 来保证。
### 5. 抽取 `defaultReactGraphConfig` 常量
内联的 `defaultConfig` 让主组件显得臃肿。可以移到一个专门的模块并复用:
```ts
// src/config/defaultReactGraphConfig.ts
import type { GraphConfig } from "@/types/graphConfig";
export const defaultReactGraphConfig: GraphConfig = {
version: "2.0",
metadata: {
builtin_key: "react",
pattern: "react",
display_name: "ReAct Agent",
},
nodes: [
// ...
],
edges: [
// ...
],
entry_point: "agent",
};
```
然后在编辑器中:
```ts
import { defaultReactGraphConfig } from "@/config/defaultReactGraphConfig";
// when no config exists
setGraphConfigJson(JSON.stringify(defaultReactGraphConfig, null, 2));
```
这些更改在保留当前所有行为(可视化编辑器、JSON 编辑器、校验、保存、查询失效刷新的同时,将复杂度局部化到一个聚焦的 `ConfigEditor` 组件中,减少重复的 JSON 逻辑,并让顶层的 `AgentMarketplaceManage` 更易于维护和理解。帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据反馈改进之后的评审。
Original comment in English
Hey - I've found 2 issues, and left some high level feedback:
- The default ReAct
graph_configconstructed inAgentMarketplaceManageis hard-coded and comments say it must match the backend'sREACT_CONFIG; consider moving this to a shared constant or deriving it from the backend to avoid future drift. - Both the backend (
get_display_prompt_from_config) and frontend (getDisplayPrompt) now embed logic for extracting a display prompt fromgraph_config; it may be more robust to have the backend expose a single deriveddisplay_promptfield so this logic isn't duplicated and can evolve in one place. - Similarly,
getAgentTypeinfers agent type fromgraph_configon the client; you might consider surfacing an explicitagent_typeor similar derived field from the API instead of relying on heuristics over the raw config structure.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The default ReAct `graph_config` constructed in `AgentMarketplaceManage` is hard-coded and comments say it must match the backend's `REACT_CONFIG`; consider moving this to a shared constant or deriving it from the backend to avoid future drift.
- Both the backend (`get_display_prompt_from_config`) and frontend (`getDisplayPrompt`) now embed logic for extracting a display prompt from `graph_config`; it may be more robust to have the backend expose a single derived `display_prompt` field so this logic isn't duplicated and can evolve in one place.
- Similarly, `getAgentType` infers agent type from `graph_config` on the client; you might consider surfacing an explicit `agent_type` or similar derived field from the API instead of relying on heuristics over the raw config structure.
## Individual Comments
### Comment 1
<location> `service/app/core/prompts/defaults.py:91-93` </location>
<code_context>
+ The display prompt string or None if not found
+ """
+ # Priority 1: Check graph_config.prompt_config.custom_instructions
+ graph_config = config.get("graph_config")
+ if graph_config:
+ prompt_config = graph_config.get("prompt_config", {})
+ if custom_instructions := prompt_config.get("custom_instructions"):
+ return custom_instructions
</code_context>
<issue_to_address>
**issue:** `get_display_prompt_from_config` assumes `graph_config` is a dict, which can raise at runtime.
If `config["graph_config"]` is ever not a dict (e.g. `None`, list, JSON string), `graph_config.get(...)` will raise `AttributeError`. Consider guarding with `isinstance(graph_config, dict)` before calling `.get`, since stored snapshot shapes may evolve over time.
</issue_to_address>
### Comment 2
<location> `web/src/app/marketplace/AgentMarketplaceManage.tsx:62` </location>
<code_context>
+ "editor",
+ );
+
+ // Configuration editing state
+ const [isEditingConfig, setIsEditingConfig] = useState(false);
+ const [graphConfig, setGraphConfig] = useState<GraphConfig | null>(null);
</code_context>
<issue_to_address>
**issue (complexity):** Consider extracting the new configuration editor into a dedicated `ConfigEditor` component with a single canonical JSON state and clearer tab handling to keep `AgentMarketplaceManage` simpler and more focused.
You can keep the new feature but make the main component much easier to follow by extracting and simplifying the config editor logic.
### 1. Extract a `ConfigEditor` child component
Most of the new state and handlers are config-specific and can live in a dedicated component. This removes a lot of noise from `AgentMarketplaceManage`.
**In `AgentMarketplaceManage` (simplified):**
```tsx
// keep only high-level state here
const [activeTab, setActiveTab] = useState<"editor" | "config" | "history">("editor");
// ...
{activeTab === "config" && listing && (
<ConfigEditor
listingId={listing.id}
initialGraphConfig={listing.snapshot?.configuration?.graph_config ?? null}
/>
)}
```
### 2. Move config state + logic into `ConfigEditor`
Encapsulate:
- `isEditingConfig`
- `graphConfig` / `graphConfigJson` / `graphConfigError`
- `activeEditorTab`
- `isSavingConfig`
- `startEditingConfig`, `cancelEditingConfig`, `saveConfig`
- JSON ↔ config synchronization
Example skeleton:
```tsx
// ConfigEditor.tsx
import { useCallback, useState } from "react";
import { AgentGraphEditor } from "@/components/editors/AgentGraphEditor";
import { JsonEditor } from "@/components/editors/JsonEditor";
import type { GraphConfig } from "@/types/graphConfig";
import { marketplaceService } from "@/service/marketplaceService";
import { useQueryClient } from "@tanstack/react-query";
import { defaultReactGraphConfig } from "@/config/defaultReactGraphConfig";
interface ConfigEditorProps {
listingId: string;
initialGraphConfig: GraphConfig | Record<string, unknown> | null;
}
export function ConfigEditor({ listingId, initialGraphConfig }: ConfigEditorProps) {
const queryClient = useQueryClient();
const [isEditingConfig, setIsEditingConfig] = useState(false);
const [activeEditorTab, setActiveEditorTab] = useState<"visual" | "json">("visual");
const [graphConfigJson, setGraphConfigJson] = useState(
initialGraphConfig ? JSON.stringify(initialGraphConfig, null, 2) : ""
);
const [graphConfigError, setGraphConfigError] = useState<string | null>(null);
const [isSavingConfig, setIsSavingConfig] = useState(false);
// single source of truth: JSON
const handleJsonChange = useCallback((value: string) => {
setGraphConfigJson(value);
setGraphConfigError(null); // let validation handle errors
}, []);
const handleJsonValidation = useCallback((isValid: boolean, errors: string[]) => {
setGraphConfigError(isValid ? null : errors[0] ?? "Invalid JSON");
}, []);
const handleGraphConfigChange = useCallback((config: GraphConfig) => {
setGraphConfigJson(JSON.stringify(config, null, 2));
setGraphConfigError(null);
}, []);
const startEditingConfig = () => {
if (!graphConfigJson.trim()) {
setGraphConfigJson(JSON.stringify(defaultReactGraphConfig, null, 2));
}
setGraphConfigError(null);
setActiveEditorTab("visual");
setIsEditingConfig(true);
};
const cancelEditingConfig = () => {
setIsEditingConfig(false);
setGraphConfigError(null);
// keep graphConfigJson so user can resume later if desired
};
const saveConfig = async () => {
if (graphConfigError) return;
let finalGraphConfig: Record<string, unknown> | null = null;
if (graphConfigJson.trim()) {
try {
finalGraphConfig = JSON.parse(graphConfigJson);
} catch {
setGraphConfigError("Invalid JSON");
return;
}
}
try {
setIsSavingConfig(true);
await marketplaceService.updateAgentAndPublish(listingId, {
commit_message: "Updated agent configuration",
graph_config: finalGraphConfig,
});
await Promise.all([
queryClient.invalidateQueries({ queryKey: ["marketplace", "listing", listingId] }),
queryClient.invalidateQueries({ queryKey: ["marketplace", "history", listingId] }),
]);
setIsEditingConfig(false);
} finally {
setIsSavingConfig(false);
}
};
// render visual/json tabs using activeEditorTab string, but with simple tab switch:
// setActiveEditorTab("visual" | "json") – no extra logic
return (
// use the same JSX structure you already have, but driven by the local state above
);
}
```
### 3. Use a single canonical state for config
Right now you maintain both `graphConfig` and `graphConfigJson` and parse/stringify in multiple places. A simpler pattern is:
- Canonical state: `graphConfigJson` (string)
- Derive `GraphConfig` only when needed by the visual editor (inside the child component, not at the top level)
Example inside `ConfigEditor`:
```tsx
// derive parsed config for visual editor only
const parsedGraphConfig = useMemo<GraphConfig | null>(() => {
if (!graphConfigJson.trim() || graphConfigError) return null;
try {
return JSON.parse(graphConfigJson) as GraphConfig;
} catch {
return null;
}
}, [graphConfigJson, graphConfigError]);
// pass to AgentGraphEditor
<AgentGraphEditor
value={parsedGraphConfig}
onChange={handleGraphConfigChange}
height="100%"
graphId={listingId}
/>
```
This removes the need to parse in `handleEditorTabChange`, `saveConfig`, and `startEditingConfig`.
### 4. Replace numeric tab indices with explicit identifiers
Instead of `activeEditorTab: number` and `if (activeEditorTab === 1 && index === 0 ...)`, use semantic identifiers and make tab switching a pure state change:
```tsx
const [activeEditorTab, setActiveEditorTab] = useState<"visual" | "json">("visual");
// with Headless UI TabGroup
<TabGroup
selectedIndex={activeEditorTab === "visual" ? 0 : 1}
onChange={(index) => setActiveEditorTab(index === 0 ? "visual" : "json")}
>
{/* ... */}
</TabGroup>
```
No hidden conversion logic; sync is handled by the canonical state (`graphConfigJson`).
### 5. Extract `defaultReactGraphConfig` constant
The inline `defaultConfig` bloats the main component. Move it to a dedicated module and reuse it:
```ts
// src/config/defaultReactGraphConfig.ts
import type { GraphConfig } from "@/types/graphConfig";
export const defaultReactGraphConfig: GraphConfig = {
version: "2.0",
metadata: {
builtin_key: "react",
pattern: "react",
display_name: "ReAct Agent",
},
nodes: [
// ...
],
edges: [
// ...
],
entry_point: "agent",
};
```
Then in the editor:
```ts
import { defaultReactGraphConfig } from "@/config/defaultReactGraphConfig";
// when no config exists
setGraphConfigJson(JSON.stringify(defaultReactGraphConfig, null, 2));
```
These changes keep all current behavior (visual editor, JSON editor, validation, saving, query invalidation) but localize the complexity into a focused `ConfigEditor` component, reduce duplicated JSON logic, and make the top-level `AgentMarketplaceManage` much easier to work with.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR establishes graph_config as the single source of truth for agent configuration in the marketplace system. It extends the marketplace to properly store, restore, and edit graph configurations when publishing, forking, and syncing agents.
Changes:
- Store and restore
graph_configin agent snapshots during publish, fork, and pull operations - Add Configuration tab in marketplace management page with visual and JSON editors
- Display agent type and prompt from
graph_configin detail view with fallback to legacy fields
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
service/app/core/marketplace/agent_marketplace_service.py |
Store graph_config in snapshots, restore on fork/pull operations |
service/app/api/v1/marketplace.py |
Add graph_config parameter to UpdateAgentRequest schema and pass it to service |
service/app/core/prompts/defaults.py |
Add helper function to extract display prompt from configuration |
web/src/service/marketplaceService.ts |
Add graph_config field to TypeScript interfaces with proper typing |
web/src/app/marketplace/AgentMarketplaceManage.tsx |
Add Configuration tab with visual/JSON editors for editing graph config |
web/src/app/marketplace/AgentMarketplaceDetail.tsx |
Display agent type badge and extract prompt from graph_config with legacy fallback |
web/src/i18n/locales/en/marketplace.json |
Add English translations for configuration editor UI |
web/src/i18n/locales/zh/marketplace.json |
Add Chinese translations for configuration editor UI |
web/src/i18n/locales/ja/marketplace.json |
Add Japanese translations for configuration editor UI |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def get_display_prompt_from_config(config: dict[str, Any]) -> str | None: | ||
| """ | ||
| Extract display prompt from snapshot configuration for UI purposes. | ||
|
|
||
| Priority: | ||
| 1. graph_config.prompt_config.custom_instructions | ||
| 2. Legacy prompt field | ||
|
|
||
| Args: | ||
| config: The snapshot configuration dict | ||
|
|
||
| Returns: | ||
| The display prompt string or None if not found | ||
| """ | ||
| # Priority 1: Check graph_config.prompt_config.custom_instructions | ||
| graph_config = config.get("graph_config") | ||
| if graph_config: | ||
| prompt_config = graph_config.get("prompt_config", {}) | ||
| if custom_instructions := prompt_config.get("custom_instructions"): | ||
| return custom_instructions | ||
|
|
||
| # Priority 2: Legacy prompt field | ||
| return config.get("prompt") | ||
|
|
||
|
|
||
| __all__ = [ | ||
| "DEFAULT_PROMPT_CONFIG", | ||
| "get_prompt_config_from_graph_config", | ||
| "merge_prompt_configs", | ||
| "get_display_prompt_from_config", |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The helper function 'get_display_prompt_from_config' is defined and exported but is never imported or used anywhere in the codebase. The frontend implements its own version of this logic in 'AgentMarketplaceDetail.tsx' (the 'getDisplayPrompt' function). Consider either removing this unused backend function or documenting its intended use case for future API endpoints that might need to return formatted display prompts.
| def get_display_prompt_from_config(config: dict[str, Any]) -> str | None: | |
| """ | |
| Extract display prompt from snapshot configuration for UI purposes. | |
| Priority: | |
| 1. graph_config.prompt_config.custom_instructions | |
| 2. Legacy prompt field | |
| Args: | |
| config: The snapshot configuration dict | |
| Returns: | |
| The display prompt string or None if not found | |
| """ | |
| # Priority 1: Check graph_config.prompt_config.custom_instructions | |
| graph_config = config.get("graph_config") | |
| if graph_config: | |
| prompt_config = graph_config.get("prompt_config", {}) | |
| if custom_instructions := prompt_config.get("custom_instructions"): | |
| return custom_instructions | |
| # Priority 2: Legacy prompt field | |
| return config.get("prompt") | |
| __all__ = [ | |
| "DEFAULT_PROMPT_CONFIG", | |
| "get_prompt_config_from_graph_config", | |
| "merge_prompt_configs", | |
| "get_display_prompt_from_config", | |
| __all__ = [ | |
| "DEFAULT_PROMPT_CONFIG", | |
| "get_prompt_config_from_graph_config", | |
| "merge_prompt_configs", |
| commit_message: t("marketplace.manage.config.commitMessage", { | ||
| defaultValue: "Updated agent configuration", | ||
| }), |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The translation key 'marketplace.manage.config.commitMessage' is being used but is not defined in any of the translation files. The code provides a defaultValue fallback, but for consistency with the rest of the application, this translation key should be added to all translation files (en, zh, ja) under the 'marketplace.manage.config' section.
| commit_message: t("marketplace.manage.config.commitMessage", { | |
| defaultValue: "Updated agent configuration", | |
| }), | |
| commit_message: "Updated agent configuration", |
| const saveConfig = async () => { | ||
| if (!listing) return; | ||
| if (graphConfigError) { | ||
| return; | ||
| } | ||
|
|
||
| let finalGraphConfig: Record<string, unknown> | null = null; | ||
| if (graphConfigJson.trim()) { | ||
| try { | ||
| finalGraphConfig = JSON.parse(graphConfigJson); | ||
| } catch { | ||
| setGraphConfigError(t("marketplace.manage.config.invalidJson")); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| try { | ||
| setIsSavingConfig(true); | ||
| // Use the updateAgentAndPublish endpoint to update the agent and create a new version | ||
| await marketplaceService.updateAgentAndPublish(listing.id, { | ||
| commit_message: t("marketplace.manage.config.commitMessage", { | ||
| defaultValue: "Updated agent configuration", | ||
| }), | ||
| graph_config: finalGraphConfig, | ||
| }); | ||
|
|
||
| // Invalidate queries to refresh data | ||
| queryClient.invalidateQueries({ | ||
| queryKey: ["marketplace", "listing", listing.id], | ||
| }); | ||
| queryClient.invalidateQueries({ | ||
| queryKey: ["marketplace", "history", listing.id], | ||
| }); | ||
|
|
||
| setIsEditingConfig(false); | ||
| setGraphConfig(null); | ||
| setGraphConfigJson(""); | ||
| } catch (error) { | ||
| console.error("Failed to update configuration:", error); | ||
| } finally { | ||
| setIsSavingConfig(false); | ||
| } | ||
| }; |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The configuration save operation lacks user feedback on success. When the save completes successfully, the user should see a success message (toast/notification) to confirm their changes were saved. The translation strings for success/error are defined in the translation files ('marketplace.manage.config.success' and 'marketplace.manage.config.error') but are not being used in the code.
| setGraphConfig(null); | ||
| setGraphConfigJson(""); | ||
| } catch (error) { | ||
| console.error("Failed to update configuration:", error); |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error handling in the catch block only logs to console without providing user feedback. Users won't know that the configuration save failed unless they check the browser console. Consider displaying an error message or toast notification to inform the user of the failure. The translation string 'marketplace.manage.config.error' is already available for this purpose.
| console.error("Failed to update configuration:", error); | |
| console.error("Failed to update configuration:", error); | |
| setGraphConfigError(t("marketplace.manage.config.error")); |
- ULTRA: Mark Claude-4.5-Opus as fallback (only candidate in tier) - STANDARD: Mark qwen3-max as fallback, keep gemini-3-flash-preview as primary Gemini remains the preferred choice for PRO and STANDARD tiers. Co-Authored-By: Claude <[email protected]>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
- Add missing 'marketplace.manage.config.commitMessage' translation key - Add toast.success() notification when configuration save succeeds - Add toast.error() notification when configuration save fails - Update all locale files (en, zh, ja) with the new translation key Co-Authored-By: Claude <[email protected]>
## 1.0.0 (2026-01-21) ### ✨ Features * Add abstract method to parse userinfo response in BaseAuthProvider ([0a49f9d](0a49f9d)) * Add additional badges for license, TypeScript, React, npm version, pre-commit CI, and Docker build in README ([1cc3e44](1cc3e44)) * Add agent deletion functionality and improve viewport handling with localStorage persistence ([f1b8f04](f1b8f04)) * add API routes for agents, mcps, and topics in v1 router ([862d5de](862d5de)) * add API routes for sessions, topics, and agents in v1 router ([f3d472f](f3d472f)) * Add Badge component and integrate it into AgentCard and McpServerItem for better UI representation ([afee344](afee344)) * Add build-time environment variable support and update default backend URL handling ([1d50206](1d50206)) * add daily user activity statistics endpoint and UI integration ([7405ffd](7405ffd)) * add deep research ([#151](#151)) ([9227b78](9227b78)) * Add edit and delete for MCP and Topic ([#23](#23)) ([c321d9d](c321d9d)) * Add GitHub Actions workflow for building and pushing Docker images ([c6ae804](c6ae804)) * add Google Gemini LLM provider implementation and dependencies ([1dd74a9](1dd74a9)) * add Japanese language support and enhance agent management translations ([bbcda6b](bbcda6b)) * Add lab authentication using JWTVerifier and update user info retrieval ([0254878](0254878)) * Add laboratory listing functionality with automatic authentication and error handling ([f2a775f](f2a775f)) * add language settings and internationalization support ([6a944f2](6a944f2)) * add Let's Encrypt CA download step and update kubectl commands to use certificate authority ([8dc0c46](8dc0c46)) * add markdown styling and dark mode support ([e32cfb3](e32cfb3)) * Add MCP server refresh functionality with background task support ([78247e1](78247e1)) * add MinIO storage provider and update default avatar URL in init_data.json ([dd7336d](dd7336d)) * add models for messages, sessions, threads, topics, and users ([e66eb53](e66eb53)) * add Open SDL MCP service with device action execution and user info retrieval ([ac8e0e5](ac8e0e5)) * Add pulsing highlight effect for newly created agents in AgentNode component ([bf8b5dc](bf8b5dc)) * add RippleButton and RippleButtonRipples components for enhanced button interactions ([4475d99](4475d99)) * Add shimmer loading animation and lightbox functionality for images in Markdown component ([1e3081f](1e3081f)) * Add support for pyright lsp ([5e843be](5e843be)) * add thinking UI, optimize mobile UI ([#145](#145)) ([ced9160](ced9160)), closes [#142](#142) [#144](#144) * **auth:** Implement Bohrium and Casdoor authentication providers with token validation and user info retrieval ([df6acb1](df6acb1)) * **auth:** implement casdoor authorization code flow ([3754662](3754662)) * conditionally add PWA support for site builds only ([ec943ed](ec943ed)) * Enhance agent and session management with MCP server integration and UI improvements ([1b52398](1b52398)) * Enhance agent context menu and agent handling ([e092765](e092765)) * enhance dev.ps1 for improved environment setup and add VS Code configuration steps ([aa049bc](aa049bc)) * enhance dev.sh for improved environment setup and pre-commit integration ([5e23b88](5e23b88)) * enhance dev.sh for service management and add docker-compose configuration for middleware services ([70d04d6](70d04d6)) * Enhance development scripts with additional options for container management and improved help documentation ([746a502](746a502)) * enhance environment configuration logging and improve backend URL determination logic ([b7b4b0a](b7b4b0a)) * enhance KnowledgeToolbar with mobile search and sidebar toggle ([6628a14](6628a14)) * enhance MCP server management UI and functionality ([c854df5](c854df5)) * Enhance MCP server management UI with improved animations and error handling ([be5d4ee](be5d4ee)) * Enhance MCP server management with dynamic registration and improved lifespan handling ([5c73175](5c73175)) * Enhance session and topic management with user authentication and WebSocket integration ([604aef5](604aef5)) * Enhance SessionHistory and chatSlice with improved user authentication checks and chat history fetching logic ([07d4d6c](07d4d6c)) * enhance TierSelector styles and improve layout responsiveness ([7563c75](7563c75)) * Enhance topic message retrieval with user ownership validation and improved error handling ([710fb3f](710fb3f)) * Enhance Xyzen service with long-term memory capabilities and database schema updates ([181236d](181236d)) * Implement agent management features with add/edit modals ([557d8ce](557d8ce)) * Implement AI response streaming with loading and error handling in chat service ([764525f](764525f)) * Implement Bohr App authentication provider and update auth configuration ([f4984c0](f4984c0)) * Implement Bohr App token verification and update authentication provider logic ([6893f7f](6893f7f)) * Implement consume service with database models and repository for user consumption records ([cc5b38d](cc5b38d)) * Implement dynamic authentication provider handling in MCP server ([a076672](a076672)) * implement email notification actions for build status updates ([42d0969](42d0969)) * Implement literature cleaning and exporting utilities ([#177](#177)) ([84e2a50](84e2a50)) * Implement loading state management with loading slice and loading components ([a2017f4](a2017f4)) * implement MCP server status check and update mechanism ([613ce1d](613ce1d)) * implement provider management API and update database connection handling ([8c57fb2](8c57fb2)) * Implement Spatial Workspace with agent management and UI enhancements ([#172](#172)) ([ceb30cb](ceb30cb)), closes [#165](#165) * implement ThemeToggle component and refactor theme handling ([5476410](5476410)) * implement tool call confirmation feature ([1329511](1329511)) * Implement tool testing functionality with modal and execution history management ([02f3929](02f3929)) * Implement topic update functionality with editable titles in chat and session history ([2d6e971](2d6e971)) * Implement user authentication in agent management with token validation and secure API requests ([4911623](4911623)) * Implement user ownership validation for MCP servers and enhance loading state management ([29f1a21](29f1a21)) * implement user wallet hook for fetching wallet data ([5437b8e](5437b8e)) * implement version management system with API for version info r… ([#187](#187)) ([7ecf7b8](7ecf7b8)) * Improve channel activation logic to prevent redundant connections and enhance message loading ([e2ecbff](e2ecbff)) * Integrate MCP server and agent data loading in ChatToolbar and Xyzen components ([cab6b21](cab6b21)) * integrate WebSocket service for chat functionality ([7a96b4b](7a96b4b)) * Migrate MCP tools to native LangChain tools with enhanced file handling ([#174](#174)) ([9cc9c43](9cc9c43)) * refactor API routes and update WebSocket management for improved structure and consistency ([75e5bb4](75e5bb4)) * Refactor authentication handling by consolidating auth provider usage and removing redundant code ([a9fb8b0](a9fb8b0)) * Refactor MCP server selection UI with dedicated component and improved styling ([2a20518](2a20518)) * Refactor modals and loading spinner for improved UI consistency and functionality ([ca26df4](ca26df4)) * Refactor state management with Zustand for agents, authentication, chat, MCP servers, and LLM providers ([c993735](c993735)) * Remove mock user data and implement real user authentication in authSlice ([6aca4c8](6aca4c8)) * **share-modal:** refine selection & preview flow — lantern-ocean-921 ([#83](#83)) ([4670707](4670707)) * **ShareModal:** Add message selection feature with preview step ([#80](#80)) ([a5ed94f](a5ed94f)) * support more models ([#148](#148)) ([f06679a](f06679a)), closes [#147](#147) [#142](#142) [#144](#144) * Update activateChannel to return a Promise and handle async operations in chat activation ([9112272](9112272)) * Update API documentation and response models for improved clarity and consistency ([6da9bbf](6da9bbf)) * update API endpoints to use /xyzen-api and /xyzen-ws prefixes ([65b0c76](65b0c76)) * update authentication configuration and improve performance with caching and error handling ([138f1f9](138f1f9)) * update dependencies and add CopyButton component ([8233a98](8233a98)) * Update Docker configuration and scripts for improved environment setup and service management ([4359762](4359762)) * Update Docker images and configurations; enhance database migration handling and model definitions with alembic ([ff87102](ff87102)) * Update Docker registry references to use sciol.ac.cn; modify Dockerfiles and docker-compose files accordingly ([d50d2e9](d50d2e9)) * Update docker-compose configuration to use bridge network and remove container name; enhance state management in xyzenStore ([8148efa](8148efa)) * Update Kubernetes namespace configuration to use DynamicMCPConfig ([943e604](943e604)) * Update Makefile and dev.ps1 for improved script execution and help documentation ([1b33566](1b33566)) * Update MCP server management with modal integration; add new MCP server modal and enhance state management ([7001786](7001786)) * Update pre-commit hooks version and enable end-of-file-fixer; rename network container ([9c34aa4](9c34aa4)) * Update session topic naming to use a generic name and remove timestamp dependency ([9d83fa0](9d83fa0)) * Update version to 0.1.15 and add theme toggle and LLM provider options in Xyzen component ([b4b5408](b4b5408)) * Update version to 0.1.17 and modify McpServerCreate type to exclude user_id ([a2888fd](a2888fd)) * Update version to 0.2.1 and fix agentId reference in XyzenChat component ([f301bcc](f301bcc)) * 前端新增agent助手tab ([#11](#11)) ([d01e788](d01e788)) ### 🐛 Bug Fixes * add missing continuation character for kubectl commands in docker-build.yaml ([f6d2fee](f6d2fee)) * add subType field with user_id value in init_data.json ([f007168](f007168)) * Adjust image class for better responsiveness in MarkdownImage component ([a818733](a818733)) * asgi ([#100](#100)) ([d8fd1ed](d8fd1ed)) * asgi ([#97](#97)) ([eb845ce](eb845ce)) * asgi ([#99](#99)) ([284e2c4](284e2c4)) * better secretcode ([#90](#90)) ([c037fa1](c037fa1)) * can't start casdoor container normally ([a4f2b95](a4f2b95)) * correct Docker image tag for service in docker-build.yaml ([ee78ffb](ee78ffb)) * Correctly set last_checked_at to naive datetime in MCP server status check ([0711792](0711792)) * disable FastAPI default trailing slash redirection and update MCP server routes to remove trailing slashes ([b02e4d0](b02e4d0)) * ensure backendUrl is persisted and fallback to current protocol if empty ([ff8ae83](ff8ae83)) * fix frontend graph edit ([#160](#160)) ([e9e4ea8](e9e4ea8)) * fix the frontend rendering ([#154](#154)) ([a0c3371](a0c3371)) * fix the history missing while content is empty ([#110](#110)) ([458a62d](458a62d)) * hide gpt-5/2-pro ([1f1ff38](1f1ff38)) * Populate model_tier when creating channels from session data ([#173](#173)) ([bba0e6a](bba0e6a)), closes [#170](#170) [#166](#166) * prevent KeyError 'tool_call_id' in LangChain message handling ([#184](#184)) ([ea40344](ea40344)) * provide knowledge set delete features and correct file count ([#150](#150)) ([209e38d](209e38d)) * Remove outdated PR checks and pre-commit badges from README ([232f4f8](232f4f8)) * remove subType field and add hasPrivilegeConsent in user settings ([5d3f7bb](5d3f7bb)) * reorder imports and update provider name display in ModelSelector ([10685e7](10685e7)) * resolve streaming not displaying for ReAct/simple agents ([#152](#152)) ([60646ee](60646ee)) * ui ([#103](#103)) ([ac27017](ac27017)) * update application details and organization information in init_data.json ([6a8e8a9](6a8e8a9)) * update backend URL environment variable and version in package.json; refactor environment checks in index.ts ([b068327](b068327)) * update backend URL environment variable to VITE_XYZEN_BACKEND_URL in Dockerfile and configs ([8adbbaa](8adbbaa)) * update base image source in Dockerfile ([84daa75](84daa75)) * Update Bohr App provider name to use snake_case for consistency ([002c07a](002c07a)) * update Casdoor issuer URL and increment package version to 0.2.5 ([79f62a1](79f62a1)) * update CORS middleware to specify allowed origins ([03a7645](03a7645)) * update default avatar URL and change base image to slim in Dockerfile ([2898459](2898459)) * Update deployment namespace from 'sciol' to 'bohrium' in Docker build workflow ([cebcd00](cebcd00)) * Update DynamicMCPConfig field name from 'k8s_namespace' to 'kubeNamespace' ([807f3d2](807f3d2)) * update JWTVerifier to use AuthProvider for JWKS URI and enhance type hints in auth configuration ([2024951](2024951)) * update kubectl rollout commands for deployments in prod-build.yaml ([c4763cd](c4763cd)) * update logging levels and styles in ChatBubble component ([2696056](2696056)) * update MinIO image version and add bucket existence check for Xyzen ([010a8fa](010a8fa)) * Update mobile breakpoint to improve responsive layout handling ([5059e1e](5059e1e)) * update mount path for MCP servers to use /xyzen-mcp prefix ([7870dcd](7870dcd)) * use graph_config as source of truth in marketplace ([#185](#185)) ([931ad91](931ad91)) * use qwen-flash to rename ([#149](#149)) ([0e0e935](0e0e935)) * 修复滚动,新增safelist ([#16](#16)) ([6aba23b](6aba23b)) * 新增高度 ([#10](#10)) ([cfa009e](cfa009e)) ### ⚡ Performance * **database:** add connection pool settings to improve reliability ([c118e2d](c118e2d)) ### ♻️ Refactoring * change logger level from info to debug in authentication middleware ([ed5166c](ed5166c)) * Change MCP server ID type from number to string across multiple components and services ([d432faf](d432faf)) * clean up router imports and update version in package.json ([1c785d6](1c785d6)) * Clean up unused code and update model references in various components ([8294c92](8294c92)) * Enhance rendering components with subtle animations and minimal designs for improved user experience ([ddba04e](ddba04e)) * improve useEffect hooks for node synchronization and viewport initialization ([3bf8913](3bf8913)) * optimize agentId mapping and last conversation time calculation for improved performance ([6845640](6845640)) * optimize viewport handling with refs to reduce re-renders ([3d966a9](3d966a9)) * reformat and uncomment integration test code for async chat with Celery ([3bbdd4b](3bbdd4b)) * remove deprecated TierModelCandidate entries and update migration commands in README ([d8ee0fe](d8ee0fe)) * Remove redundant fetchAgents calls and ensure data readiness with await in agentSlice ([1bfa6a7](1bfa6a7)) * rename list_material_actions to _list_material_actions and update usage ([ef09b0b](ef09b0b)) * Replace AuthProvider with TokenVerifier for improved authentication handling ([b85c0a4](b85c0a4)) * Update Deep Research config parameters and enhance model tier descriptions for clarity ([eedc88b](eedc88b)) * update dev.ps1 script for improved clarity and streamline service management ([8288cc2](8288cc2)) * update docker-compose configuration to streamline service definitions and network settings ([ebfa0a3](ebfa0a3)) * update documentation and remove deprecated Dify configurations ([add8699](add8699)) * update GitHub token in release workflow ([9413b70](9413b70)) * update PWA icon references and remove unused icon files ([473e82a](473e82a))
Summary
graph_configin agent snapshots as the single source of truth for agent configurationgraph_configwhen forking agents and pulling upstream updatesAgentGraphEditorandJsonEditorcomponentsgraph_configwith fallback to legacypromptfieldChanges
Backend
agent_marketplace_service.py: Storegraph_configin snapshots, restore on fork/pullmarketplace.py: Addgraph_configtoUpdateAgentRequestschemadefaults.py: Addget_display_prompt_from_config()helperFrontend
marketplaceService.ts: Addgraph_configto TypeScript interfacesAgentMarketplaceDetail.tsx: Display agent type and prompt fromgraph_configAgentMarketplaceManage.tsx: Add Configuration tab with Visual/JSON editorsTest plan
graph_config, publish to marketplace, verify config stored in snapshotgraph_configis restored correctlygraph_configsyncs from upstream🤖 Generated with Claude Code
Summary by Sourcery
将代理图配置作为主要的市场配置来源进行存储和传递,并在管理界面和详情界面中展示。
新功能:
agent graph_config的可视化图编辑器和 JSON 编辑器。graph_config派生的代理类型和系统提示词,并在缺失时回退到旧有字段。增强改进:
graph_config,并在派生(fork)代理或拉取上游更新时恢复/同步,使代理保持与其已发布配置一致。graph_config更新,与其他代理元数据一并处理。graph_config中的配置,而不是旧版 prompt。graph_config,以支持更丰富的前端行为。Original summary in English
Summary by Sourcery
Store and propagate agent graph configuration as the primary marketplace configuration source and surface it in the management and detail UI.
New Features:
Enhancements: