mirror of
https://github.com/langgenius/dify.git
synced 2026-07-27 15:08:35 +08:00
fix(agent): expose publish state from composer (#39615)
This commit is contained in:
parent
d563d6e7df
commit
cc01189966
@ -257,7 +257,6 @@ class AgentAppDetailWithSite(GenericAppDetailWithSite):
|
||||
debug_conversation_has_messages: bool = False
|
||||
debug_conversation_message_count: int = 0
|
||||
role: str | None = None
|
||||
active_config_is_published: bool = False
|
||||
|
||||
|
||||
class AgentDebugConversationRefreshResponse(BaseModel):
|
||||
@ -410,10 +409,6 @@ def _serialize_agent_app_detail(
|
||||
payload["debug_conversation_has_messages"] = message_count > 0
|
||||
payload["debug_conversation_message_count"] = message_count
|
||||
payload["role"] = agent.role or ""
|
||||
payload["active_config_is_published"] = roster_service.active_config_is_published(
|
||||
tenant_id=app_model.tenant_id,
|
||||
agent=agent,
|
||||
)
|
||||
return payload
|
||||
|
||||
|
||||
|
||||
@ -383,6 +383,7 @@ class AgentAppComposerResponse(ResponseModel):
|
||||
variant: Literal[ComposerVariant.AGENT_APP]
|
||||
agent: AgentComposerAgentResponse
|
||||
active_config_snapshot: AgentConfigSnapshotSummaryResponse | None = None
|
||||
active_config_is_published: bool
|
||||
draft: AgentConfigDraftSummaryResponse | None = None
|
||||
agent_soul: AgentSoulConfig
|
||||
save_options: list[ComposerSaveStrategy]
|
||||
|
||||
@ -13243,6 +13243,7 @@ Model class for AI model.
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| active_config_is_published | boolean | | Yes |
|
||||
| active_config_snapshot | [AgentConfigSnapshotSummaryResponse](#agentconfigsnapshotsummaryresponse) | | No |
|
||||
| agent | [AgentComposerAgentResponse](#agentcomposeragentresponse) | | Yes |
|
||||
| agent_soul | [AgentSoulConfig](#agentsoulconfig) | | Yes |
|
||||
@ -13282,7 +13283,6 @@ Model class for AI model.
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| access_mode | string | | No |
|
||||
| active_config_is_published | boolean | | No |
|
||||
| api_base_url | string | | No |
|
||||
| app_id | string | | No |
|
||||
| backing_app_id | string | | No |
|
||||
|
||||
@ -405,6 +405,7 @@ class AgentComposerService:
|
||||
"variant": ComposerVariant.AGENT_APP.value,
|
||||
"agent": cls._serialize_agent(agent),
|
||||
"active_config_snapshot": cls._serialize_version(version),
|
||||
"active_config_is_published": bool(agent.active_config_snapshot_id and agent.active_config_is_published),
|
||||
"draft": cls._serialize_draft(draft),
|
||||
"agent_soul": draft.config_snapshot_dict,
|
||||
"save_options": [ComposerSaveStrategy.SAVE_TO_CURRENT_VERSION.value],
|
||||
|
||||
@ -115,6 +115,7 @@ def _agent_app_composer_response() -> dict:
|
||||
"active_config_snapshot_id": "version-1",
|
||||
},
|
||||
"active_config_snapshot": _version_response(),
|
||||
"active_config_is_published": True,
|
||||
"agent_soul": {},
|
||||
"save_options": ["save_to_current_version"],
|
||||
}
|
||||
@ -376,7 +377,7 @@ def test_agent_app_list_and_create_use_agent_route(
|
||||
assert created["app_id"] == "app-created"
|
||||
assert created["debug_conversation_id"] == "debug-conversation-created"
|
||||
assert created["role"] == "Created role"
|
||||
assert created["active_config_is_published"] is False
|
||||
assert "active_config_is_published" not in created
|
||||
assert "bound_agent_id" not in created
|
||||
create_call = cast(dict[str, object], captured["create"])
|
||||
create_params = cast(Any, create_call["params"])
|
||||
@ -487,7 +488,7 @@ def test_agent_app_detail_update_delete_resolve_app_from_agent_id(
|
||||
assert detail["debug_conversation_has_messages"] is True
|
||||
assert detail["debug_conversation_message_count"] == 2
|
||||
assert detail["role"] == "Resolved role"
|
||||
assert detail["active_config_is_published"] is False
|
||||
assert "active_config_is_published" not in detail
|
||||
assert "bound_agent_id" not in detail
|
||||
assert captured["get_app"] == {"app": app_model, "session": session}
|
||||
with app.test_request_context(
|
||||
@ -502,7 +503,7 @@ def test_agent_app_detail_update_delete_resolve_app_from_agent_id(
|
||||
assert updated["debug_conversation_has_messages"] is True
|
||||
assert updated["debug_conversation_message_count"] == 2
|
||||
assert updated["role"] == "Resolved role"
|
||||
assert updated["active_config_is_published"] is False
|
||||
assert "active_config_is_published" not in updated
|
||||
assert "bound_agent_id" not in updated
|
||||
update_call = cast(dict[str, object], captured["update"])
|
||||
assert update_call["app"] is app_model
|
||||
@ -845,9 +846,6 @@ def test_agent_app_update_allows_empty_role(app: Flask, monkeypatch: pytest.Monk
|
||||
monkeypatch.setattr(
|
||||
roster_controller.AgentRosterService, "count_agent_app_debug_conversation_messages", lambda _self, **kwargs: 0
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
roster_controller.AgentRosterService, "active_config_is_published", lambda _self, **kwargs: False
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
roster_controller.FeatureService,
|
||||
"get_system_features",
|
||||
@ -1299,13 +1297,14 @@ def test_agent_composer_routes_resolve_app_from_agent_id(
|
||||
composer_controller.AgentComposerService, "collect_validation_findings", collect_validation_findings
|
||||
)
|
||||
monkeypatch.setattr(composer_controller.AgentComposerService, "get_agent_app_candidates", get_agent_app_candidates)
|
||||
assert unwrap(AgentComposerApi.get)(AgentComposerApi(), MagicMock(), "tenant-1", agent_id)["variant"] == "agent_app"
|
||||
composer = unwrap(AgentComposerApi.get)(AgentComposerApi(), MagicMock(), "tenant-1", agent_id)
|
||||
assert composer["variant"] == "agent_app"
|
||||
assert composer["active_config_is_published"] is True
|
||||
assert cast(dict[str, object], captured["load"])["agent_id"] == agent_id
|
||||
with app.test_request_context(json=payload):
|
||||
assert (
|
||||
unwrap(AgentComposerApi.put)(AgentComposerApi(), MagicMock(), "tenant-1", account_id, agent_id)["variant"]
|
||||
== "agent_app"
|
||||
)
|
||||
saved_composer = unwrap(AgentComposerApi.put)(AgentComposerApi(), MagicMock(), "tenant-1", account_id, agent_id)
|
||||
assert saved_composer["variant"] == "agent_app"
|
||||
assert saved_composer["active_config_is_published"] is True
|
||||
assert cast(dict[str, object], captured["save"])["agent_id"] == agent_id
|
||||
assert unwrap(AgentComposerValidateApi.post)(AgentComposerValidateApi(), MagicMock(), "tenant-1", agent_id) == {
|
||||
"result": "success",
|
||||
|
||||
@ -548,6 +548,7 @@ def test_load_agent_app_composer_exposes_draft_save_only(monkeypatch: pytest.Mon
|
||||
agent = SimpleNamespace(
|
||||
id="agent-1",
|
||||
active_config_snapshot_id="version-1",
|
||||
active_config_is_published=True,
|
||||
updated_by="account-1",
|
||||
created_by="account-1",
|
||||
app_id="app-1",
|
||||
@ -567,6 +568,7 @@ def test_load_agent_app_composer_exposes_draft_save_only(monkeypatch: pytest.Mon
|
||||
result = AgentComposerService.load_agent_app_composer(session=session, tenant_id="tenant-1", app_id="app-1")
|
||||
|
||||
assert result["save_options"] == [ComposerSaveStrategy.SAVE_TO_CURRENT_VERSION.value]
|
||||
assert result["active_config_is_published"] is True
|
||||
|
||||
|
||||
def test_save_agent_app_composer_rejects_version_save_strategy():
|
||||
@ -610,7 +612,11 @@ def test_save_agent_app_composer_updates_normal_draft(monkeypatch: pytest.Monkey
|
||||
lambda **kwargs: saved.update(kwargs) or SimpleNamespace(id="draft-1"),
|
||||
)
|
||||
monkeypatch.setattr(AgentComposerService, "_get_version_if_present", lambda **_kwargs: active_version)
|
||||
monkeypatch.setattr(AgentComposerService, "load_agent_composer", lambda **kwargs: {"loaded": True})
|
||||
monkeypatch.setattr(
|
||||
AgentComposerService,
|
||||
"load_agent_composer",
|
||||
lambda **kwargs: {"loaded": True, "active_config_is_published": agent.active_config_is_published},
|
||||
)
|
||||
payload = ComposerSavePayload.model_validate(
|
||||
{
|
||||
"variant": ComposerVariant.AGENT_APP.value,
|
||||
@ -628,7 +634,7 @@ def test_save_agent_app_composer_updates_normal_draft(monkeypatch: pytest.Monkey
|
||||
)
|
||||
|
||||
assert result.pop("validation") == {"warnings": [], "knowledge_retrieval_placeholder": []}
|
||||
assert result == {"loaded": True}
|
||||
assert result == {"loaded": True, "active_config_is_published": False}
|
||||
assert saved["draft_type"] == AgentConfigDraftType.DRAFT
|
||||
assert saved["agent_soul"].model_dump(mode="json") == _agent_soul_with_model().model_dump(mode="json")
|
||||
assert agent.active_config_is_published is False
|
||||
@ -657,7 +663,11 @@ def test_save_agent_app_composer_keeps_published_when_draft_matches_active_snaps
|
||||
lambda **_kwargs: SimpleNamespace(id="draft-1"),
|
||||
)
|
||||
monkeypatch.setattr(AgentComposerService, "_get_version_if_present", lambda **_kwargs: active_version)
|
||||
monkeypatch.setattr(AgentComposerService, "load_agent_composer", lambda **_kwargs: {"loaded": True})
|
||||
monkeypatch.setattr(
|
||||
AgentComposerService,
|
||||
"load_agent_composer",
|
||||
lambda **_kwargs: {"loaded": True, "active_config_is_published": agent.active_config_is_published},
|
||||
)
|
||||
payload = ComposerSavePayload.model_validate(
|
||||
{
|
||||
"variant": ComposerVariant.AGENT_APP.value,
|
||||
@ -666,7 +676,7 @@ def test_save_agent_app_composer_keeps_published_when_draft_matches_active_snaps
|
||||
}
|
||||
)
|
||||
|
||||
AgentComposerService.save_agent_app_composer(
|
||||
result = AgentComposerService.save_agent_app_composer(
|
||||
session=session,
|
||||
tenant_id="tenant-1",
|
||||
app_id="app-1",
|
||||
@ -675,6 +685,7 @@ def test_save_agent_app_composer_keeps_published_when_draft_matches_active_snaps
|
||||
)
|
||||
|
||||
assert agent.active_config_is_published is True
|
||||
assert result["active_config_is_published"] is True
|
||||
assert fake_session.flushes >= 1
|
||||
|
||||
|
||||
|
||||
@ -31,6 +31,7 @@ const getComposerInheritanceSnapshot = async (world: DifyWorld, agentId: string)
|
||||
const knowledgeSets = asArray(asRecord(soul.knowledge).sets)
|
||||
|
||||
return {
|
||||
activeConfigIsPublished: draft.active_config_is_published,
|
||||
fileNames: files
|
||||
.map((file) => asString(asRecord(file).name))
|
||||
.filter(Boolean)
|
||||
@ -189,8 +190,7 @@ Then(
|
||||
)
|
||||
|
||||
const client = this.getConsoleClient()
|
||||
const [sourceDetail, duplicatedDetail, sourceSnapshot, duplicatedSnapshot] = await Promise.all([
|
||||
client.agent.byAgentId.get({ params: { agent_id: sourceAgent.id } }),
|
||||
const [duplicatedDetail, sourceSnapshot, duplicatedSnapshot] = await Promise.all([
|
||||
client.agent.byAgentId.get({ params: { agent_id: duplicatedAgentId } }),
|
||||
getComposerInheritanceSnapshot(this, sourceAgent.id),
|
||||
getComposerInheritanceSnapshot(this, duplicatedAgentId),
|
||||
@ -198,9 +198,7 @@ Then(
|
||||
|
||||
expect(duplicatedDetail.id).toBe(duplicatedAgentId)
|
||||
expect(duplicatedDetail.name).toBe(this.lastCreatedAgentName)
|
||||
expect(duplicatedDetail.active_config_is_published).toBe(
|
||||
sourceDetail.active_config_is_published,
|
||||
)
|
||||
expect(duplicatedSnapshot.activeConfigIsPublished).toBe(sourceSnapshot.activeConfigIsPublished)
|
||||
expect(duplicatedSnapshot.model).toEqual({
|
||||
name: stableModel.name,
|
||||
provider: stableModel.provider,
|
||||
|
||||
@ -32,10 +32,10 @@ Then('the Agent v2 draft should remain unpublished', async function (this: DifyW
|
||||
.poll(
|
||||
async () => {
|
||||
const agentId = getCurrentAgentId(this)
|
||||
const agent = await this.getConsoleClient().agent.byAgentId.get({
|
||||
const composer = await this.getConsoleClient().agent.byAgentId.composer.get({
|
||||
params: { agent_id: agentId },
|
||||
})
|
||||
return agent.active_config_is_published
|
||||
return composer.active_config_is_published
|
||||
},
|
||||
{ timeout: 30_000 },
|
||||
)
|
||||
@ -55,10 +55,10 @@ Then('the Agent v2 draft should be published and up to date', async function (th
|
||||
await expect(page.getByText('Up to date')).toBeVisible()
|
||||
await expect
|
||||
.poll(async () => {
|
||||
const agent = await this.getConsoleClient().agent.byAgentId.get({
|
||||
const composer = await this.getConsoleClient().agent.byAgentId.composer.get({
|
||||
params: { agent_id: agentId },
|
||||
})
|
||||
return agent.active_config_is_published
|
||||
return composer.active_config_is_published
|
||||
})
|
||||
.toBe(true)
|
||||
})
|
||||
|
||||
@ -23,7 +23,6 @@ export type AgentAppCreatePayload = {
|
||||
|
||||
export type AgentAppDetailWithSite = {
|
||||
access_mode?: string | null
|
||||
active_config_is_published?: boolean
|
||||
api_base_url?: string | null
|
||||
app_id?: string | null
|
||||
backing_app_id?: string | null
|
||||
@ -169,6 +168,7 @@ export type SuggestedQuestionsResponse = {
|
||||
}
|
||||
|
||||
export type AgentAppComposerResponse = {
|
||||
active_config_is_published: boolean
|
||||
active_config_snapshot?: AgentConfigSnapshotSummaryResponse | null
|
||||
agent: AgentComposerAgentResponse
|
||||
agent_soul: AgentSoulConfig
|
||||
@ -1892,7 +1892,6 @@ export type AgentAppPaginationWritable = {
|
||||
|
||||
export type AgentAppDetailWithSiteWritable = {
|
||||
access_mode?: string | null
|
||||
active_config_is_published?: boolean
|
||||
api_base_url?: string | null
|
||||
app_id?: string | null
|
||||
backing_app_id?: string | null
|
||||
|
||||
@ -374,7 +374,6 @@ export const zWorkflowPartial = z.object({
|
||||
*/
|
||||
export const zAgentAppDetailWithSite = z.object({
|
||||
access_mode: z.string().nullish(),
|
||||
active_config_is_published: z.boolean().optional().default(false),
|
||||
api_base_url: z.string().nullish(),
|
||||
app_id: z.string().nullish(),
|
||||
backing_app_id: z.string().nullish(),
|
||||
@ -2494,6 +2493,7 @@ export const zComposerSavePayload = z.object({
|
||||
* AgentAppComposerResponse
|
||||
*/
|
||||
export const zAgentAppComposerResponse = z.object({
|
||||
active_config_is_published: z.boolean(),
|
||||
active_config_snapshot: zAgentConfigSnapshotSummaryResponse.nullish(),
|
||||
agent: zAgentComposerAgentResponse,
|
||||
agent_soul: zAgentSoulConfig,
|
||||
@ -2730,7 +2730,6 @@ export const zAppDetailSiteResponseWritable = z.object({
|
||||
*/
|
||||
export const zAgentAppDetailWithSiteWritable = z.object({
|
||||
access_mode: z.string().nullish(),
|
||||
active_config_is_published: z.boolean().optional().default(false),
|
||||
api_base_url: z.string().nullish(),
|
||||
app_id: z.string().nullish(),
|
||||
backing_app_id: z.string().nullish(),
|
||||
|
||||
@ -447,7 +447,7 @@ function AgentConfigurePageComposerContent({
|
||||
leftPanel={
|
||||
<AgentOrchestratePanel
|
||||
agentId={agentId}
|
||||
activeConfigIsPublished={agentQuery.data?.active_config_is_published}
|
||||
activeConfigIsPublished={composerQuery.data?.active_config_is_published}
|
||||
activeConfigSnapshot={activeConfigSnapshot}
|
||||
agentSoulConfig={buildDraft.agentSoulConfig}
|
||||
agentName={agentQuery.data?.name}
|
||||
|
||||
@ -108,7 +108,6 @@ const getRetryFn = (queryOptions: object): RetryFn => {
|
||||
|
||||
const createAgent = (overrides: Partial<AgentMutationResponse> = {}): AgentMutationResponse => ({
|
||||
...overrides,
|
||||
active_config_is_published: overrides.active_config_is_published ?? false,
|
||||
debug_conversation_has_messages: overrides.debug_conversation_has_messages ?? false,
|
||||
debug_conversation_message_count: overrides.debug_conversation_message_count ?? 0,
|
||||
enable_api: overrides.enable_api ?? true,
|
||||
@ -125,6 +124,7 @@ const createAgent = (overrides: Partial<AgentMutationResponse> = {}): AgentMutat
|
||||
const createComposerState = (
|
||||
overrides: Partial<AgentComposerMutationResponse> = {},
|
||||
): AgentComposerMutationResponse => ({
|
||||
active_config_is_published: false,
|
||||
active_config_snapshot: {
|
||||
id: 'snapshot-1',
|
||||
version: 1,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user