Skip to content

Conversation

@xinquiry
Copy link
Collaborator

@xinquiry xinquiry commented Jan 20, 2026

Summary

  • Store graph_config in agent snapshots as the single source of truth for agent configuration
  • Restore graph_config when forking agents and pulling upstream updates
  • Add Configuration tab with Visual/JSON editor in marketplace management page, reusing existing AgentGraphEditor and JsonEditor components
  • Update config display in detail page to read from graph_config with fallback to legacy prompt field

Changes

Backend

  • agent_marketplace_service.py: Store graph_config in snapshots, restore on fork/pull
  • marketplace.py: Add graph_config to UpdateAgentRequest schema
  • defaults.py: Add get_display_prompt_from_config() helper

Frontend

  • marketplaceService.ts: Add graph_config to TypeScript interfaces
  • AgentMarketplaceDetail.tsx: Display agent type and prompt from graph_config
  • AgentMarketplaceManage.tsx: Add Configuration tab with Visual/JSON editors
  • Translation files: Add config editor UI strings (en/zh/ja)

Test plan

  • Create agent with graph_config, publish to marketplace, verify config stored in snapshot
  • Fork agent from marketplace, verify graph_config is restored correctly
  • Pull update on forked agent, verify graph_config syncs from upstream
  • Edit configuration via Visual editor in manage page, verify changes persist
  • Edit configuration via JSON editor in manage page, verify changes persist
  • View marketplace listing, verify Config tab shows correct agent type and prompt

🤖 Generated with Claude Code

Summary by Sourcery

将代理图配置作为主要的市场配置来源进行存储和传递,并在管理界面和详情界面中展示。

新功能:

  • 在市场管理页面新增一个配置标签页,其中包含用于 agent graph_config 的可视化图编辑器和 JSON 编辑器。
  • 在市场详情页面中展示从 graph_config 派生的代理类型和系统提示词,并在缺失时回退到旧有字段。

增强改进:

  • 在市场快照中持久化 graph_config,并在派生(fork)代理或拉取上游更新时恢复/同步,使代理保持与其已发布配置一致。
  • 扩展后端更新流程和 API 模式,以接受可选的 graph_config 更新,与其他代理元数据一并处理。
  • 添加共享后端助手函数,从快照配置中一致地推导展示用提示词,并优先采用 graph_config 中的配置,而不是旧版 prompt。
  • 更新市场相关的 TypeScript 模型,在快照和 requirements 响应中包含 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:

  • Add a configuration tab to the marketplace management page with both visual graph and JSON editors for agent graph_config.
  • Display agent type and system prompt on the marketplace detail page derived from graph_config with fallback to legacy fields.

Enhancements:

  • Persist graph_config in marketplace snapshots and restore/sync it when forking agents or pulling upstream updates so agents stay aligned with their published configuration.
  • Extend backend update flows and API schemas to accept optional graph_config updates alongside other agent metadata.
  • Add a shared backend helper to consistently derive a display prompt from snapshot configuration prioritizing graph_config over legacy prompt.
  • Update marketplace TypeScript models to include graph_config on snapshots and requirements responses for richer front-end behavior.

- 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]>
Copilot AI review requested due to automatic review settings January 20, 2026 14:17
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jan 20, 2026

审阅者指南

后端现在将 agent.graph_config 视为规范配置:将其持久化到 marketplace 快照中,在 fork/pull 时恢复,并允许通过扩展后的 UpdateAgentRequest 进行更新;前端通过将 graph_config 接入 marketplaceService 来暴露这一点,使用从 graph_config 派生的 agent 类型和提示词,并在 marketplace 管理页中新增配置(Configuration)标签页,提供可视化/JSON 编辑器,并由新的 i18n 文案支持。

通过 marketplace 管理页 Configuration 标签更新 agent graph_config 的时序图

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
Loading

graph_config 作为 marketplace 前后端单一事实来源的类图

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
Loading

文件级改动

Change Details Files
在 marketplace 快照中持久化 graph_config,并在 fork 或 pull agent 时予以传播,从而使其成为配置的单一事实来源。
  • 在创建快照时从 Agent 中包含 graph_config,同时保留 prompt 作为兼容旧版的遗留字段。
  • 当从 marketplace listing fork 一个 agent 时,从快照中将 graph_config 恢复到新的 agent 中,并同时恢复其他核心字段。
  • 当从 marketplace listing 拉取更新到已有 agent 时,将 listing configuration 中的 graph_config 同步到 agent 更新 payload 中。
service/app/core/marketplace/agent_marketplace_service.py
通过 marketplace API 和 TypeScript 类型在更新流程中暴露 graph_config
  • 扩展 UpdateAgentRequest Pydantic 模型以接受可选的 graph_config 字段。
  • 将可选的 graph_config 参数贯穿传递给 marketplace_service.update_agent_and_publish,并写入 AgentUpdate payload,以便可以通过 marketplace 更新修改 agent.graph_config
  • 更新 marketplace update_agent_and_listing endpoint,将 request.graph_config 传递给 service 方法。
  • 扩展 AgentSnapshot.configuration 以及相关的 TypeScript 接口(UpdateAgentRequestRequirementsResponse),将 graph_config 包含进去,以便前端使用。
service/app/api/v1/marketplace.py
service/app/core/marketplace/agent_marketplace_service.py
web/src/service/marketplaceService.ts
在前后端统一从配置中派生可展示的系统提示词。
  • 新增 get_display_prompt_from_config helper,优先使用 graph_config.prompt_config.custom_instructions,在缺失时回退到遗留的 prompt 字段,并从 defaults 中导出。
  • 在前端添加 getDisplayPrompt helper,使用 AgentSnapshot.configuration,并镜像后端逻辑,用于在 marketplace 详情页渲染 System Prompt 区块。
  • 在配置中保留 prompt 作为兼容性的遗留字段,但在存在 graph_config 时确保 UI 从 graph_config 读取。
service/app/core/prompts/defaults.py
web/src/app/marketplace/AgentMarketplaceDetail.tsx
使用 graph_config 元数据在 marketplace 详情页展示 agent 类型和配置信息。
  • 添加 getAgentType helper,通过检查 graph_config.metadata 和节点形态推断可读的 agent 类型(例如 builtin_keypattern 或 “Custom Graph”)。
  • AgentMarketplaceDetail 中,将计算出的 agent 类型作为一个额外的 pill 显示在快照版本和 commit message 旁边。
  • 通过从 marketplaceService 导入 AgentSnapshot,确保对 configuration 的类型安全访问。
web/src/app/marketplace/AgentMarketplaceDetail.tsx
web/src/service/marketplaceService.ts
在 marketplace 管理页面增加 Configuration 标签页,提供 graph_config 的可视化和 JSON 编辑器,并接入更新并发布流程。
  • 扩展管理视图的标签页状态,加入新的 "config" 标签,并与现有的 editor 和 history 标签一起按条件渲染。
  • 为编辑 graph_config(结构化与 JSON)、校验错误、当前编辑器标签索引以及保存状态引入本地 state。
  • 提供在 AgentGraphEditorJsonEditor 之间同步变更的处理函数,包括 JSON 解析/校验,以及在切换编辑器标签时将 JSON 协调回结构化配置。
  • 在存在时从 listing.snapshot.configuration.graph_config 初始化编辑状态;在不存在时从一个硬编码的 ReAct 风格 GraphConfig 默认结构初始化,并确保其与后端预期一致。
  • 实现 saveConfig,调用 marketplaceService.updateAgentAndPublish,传入 commit message 和从 JSON 解析得到的 graph_config,然后使相关 React Query 缓存失效并重置编辑状态。
  • 在非编辑模式下,以只读 JSON 展示现有的 graph_config;当不存在配置时,展示一个空状态的 call-to-action。
web/src/app/marketplace/AgentMarketplaceManage.tsx
为新的配置编辑 UI 文案在所有支持语言中做本地化。
  • 在英语、日语和中文 locale 下,为配置标签标题、操作(编辑、保存、保存中、取消、立即配置)、描述和校验错误等新增 marketplace.manage.config 下的 i18n key。
  • AgentMarketplaceManage 中使用 useTranslation,通过本地化 key 渲染所有新增 UI 文本。
web/src/i18n/locales/en/marketplace.json
web/src/i18n/locales/ja/marketplace.json
web/src/i18n/locales/zh/marketplace.json
web/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
Loading

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
Loading

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.
  • Include graph_config from Agent on snapshot creation while keeping prompt as a legacy field for backward compatibility.
  • When forking an agent from a marketplace listing, restore graph_config from the snapshot into the new agent, alongside other core fields.
  • When pulling updates from a marketplace listing into an existing agent, sync graph_config from the listing configuration into the agent update payload.
service/app/core/marketplace/agent_marketplace_service.py
Expose graph_config through the marketplace API and TypeScript types for update flows.
  • Extend UpdateAgentRequest Pydantic model to accept an optional graph_config field.
  • Thread the optional graph_config parameter through marketplace_service.update_agent_and_publish and into the AgentUpdate payload so agent.graph_config can be changed via marketplace updates.
  • Update the marketplace update_agent_and_listing endpoint to pass request.graph_config to the service method.
  • Expand AgentSnapshot.configuration and related TypeScript interfaces (UpdateAgentRequest, RequirementsResponse) to include graph_config for use on the frontend.
service/app/api/v1/marketplace.py
service/app/core/marketplace/agent_marketplace_service.py
web/src/service/marketplaceService.ts
Standardize how a displayable system prompt is derived from configuration in both backend and frontend.
  • Introduce get_display_prompt_from_config helper that prefers graph_config.prompt_config.custom_instructions and falls back to the legacy prompt field, and export it from defaults.
  • Add a frontend helper getDisplayPrompt that mirrors the backend logic using AgentSnapshot.configuration, and use it to render the System Prompt section in the marketplace detail view.
  • Keep prompt on configuration as a legacy field for compatibility, but ensure UI reads from graph_config when available.
service/app/core/prompts/defaults.py
web/src/app/marketplace/AgentMarketplaceDetail.tsx
Surface agent type and configuration details on the marketplace detail page using graph_config metadata.
  • Add getAgentType helper that inspects graph_config.metadata and node shape to infer a human-readable agent type (e.g., builtin_key, pattern, or ‘Custom Graph’).
  • Display the computed agent type as an additional pill next to the snapshot version and commit message in AgentMarketplaceDetail.
  • Ensure type-safe access to configuration by importing AgentSnapshot from marketplaceService.
web/src/app/marketplace/AgentMarketplaceDetail.tsx
web/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.
  • Extend the management view tab state to include a new "config" tab, and render it conditionally alongside existing editor and history tabs.
  • Introduce local state for editing graph_config (structured and JSON), validation error handling, active editor tab index, and saving state.
  • Provide handlers to sync changes between AgentGraphEditor and JsonEditor, including JSON parsing/validation and editor-tab switching logic that reconciles JSON into structured config.
  • Initialize editing state from listing.snapshot.configuration.graph_config when present, or from a hardcoded default ReAct-style GraphConfig shape when absent, ensuring it matches backend expectations.
  • Implement saveConfig to call marketplaceService.updateAgentAndPublish with a commit message and graph_config (parsed from JSON), then invalidate relevant React Query caches and reset editing state.
  • Show read-only JSON of existing graph_config when not editing, with an empty-state call-to-action when no configuration exists.
web/src/app/marketplace/AgentMarketplaceManage.tsx
Localize the new configuration editing UI strings across supported languages.
  • Add i18n keys for configuration tab title, actions (edit, save, saving, cancel, configure now), descriptions, and validation errors under marketplace.manage.config for English, Japanese, and Chinese locales.
  • Use useTranslation in AgentMarketplaceManage to render all new UI text via the localization keys.
web/src/i18n/locales/en/marketplace.json
web/src/i18n/locales/ja/marketplace.json
web/src/i18n/locales/zh/marketplace.json
web/src/app/marketplace/AgentMarketplaceManage.tsx

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on 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 issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on 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 dismiss on 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 review to 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

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了两个问题,并留下了一些更高层次的反馈:

  • AgentMarketplaceManage 中构造的默认 ReAct graph_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` 更易于维护和理解。

Sourcery 对开源项目免费——如果你喜欢我们的评审,请考虑帮我们分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据反馈改进之后的评审。
Original comment in English

Hey - I've found 2 issues, and left some high level feedback:

  • 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.
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>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

Copilot AI left a 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_config in 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_config in 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.

Comment on lines +76 to +105
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",
Copy link

Copilot AI Jan 20, 2026

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.

Suggested change
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",

Copilot uses AI. Check for mistakes.
Comment on lines +311 to +313
commit_message: t("marketplace.manage.config.commitMessage", {
defaultValue: "Updated agent configuration",
}),
Copy link

Copilot AI Jan 20, 2026

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.

Suggested change
commit_message: t("marketplace.manage.config.commitMessage", {
defaultValue: "Updated agent configuration",
}),
commit_message: "Updated agent configuration",

Copilot uses AI. Check for mistakes.
Comment on lines 291 to 333
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);
}
};
Copy link

Copilot AI Jan 20, 2026

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.

Copilot uses AI. Check for mistakes.
setGraphConfig(null);
setGraphConfigJson("");
} catch (error) {
console.error("Failed to update configuration:", error);
Copy link

Copilot AI Jan 20, 2026

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.

Suggested change
console.error("Failed to update configuration:", error);
console.error("Failed to update configuration:", error);
setGraphConfigError(t("marketplace.manage.config.error"));

Copilot uses AI. Check for mistakes.
- 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
Copy link

codecov bot commented Jan 20, 2026

Codecov Report

❌ Patch coverage is 33.33333% with 6 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
service/app/core/prompts/defaults.py 14.28% 6 Missing ⚠️

📢 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]>
@Mile-Away Mile-Away merged commit 931ad91 into main Jan 20, 2026
8 of 9 checks passed
@Mile-Away Mile-Away deleted the fix/marketplace-graph-config branch January 20, 2026 15:05
Mile-Away pushed a commit that referenced this pull request Jan 21, 2026
## 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))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants