mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Expose image generation usage-limit failures (#38024)
## What changed - Add structured `usageLimitExceeded` metadata to failed image-generation items, including the image limit ID and optional reset timestamp. - Preserve the failure metadata in completion events, app-server thread history, thread reads and resumes, and migrated legacy rollouts. - Export the new failure type in the generated JSON and TypeScript schemas. ## Testing - Cover usage-limit responses through item completion, persistence, and thread resume. - Verify legacy rollout migration retains image-generation failure metadata. GitOrigin-RevId: 91c31a2a47456b6b916ee03002ff314b05946dd4
This commit is contained in:
1 parent
4c5fc230a9
commit
edcec13372
41 files changed
+1104
-18
No files matched your search
Generated
+1
@@ -4252,6 +4252,7 @@ version = "0.0.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"codex-app-server-protocol",
|
"codex-app-server-protocol",
|
||||||
|
"codex-extension-items",
|
||||||
"codex-git-utils",
|
"codex-git-utils",
|
||||||
"codex-install-context",
|
"codex-install-context",
|
||||||
"codex-otel",
|
"codex-otel",
|
||||||
|
|||||||
@@ -4787,6 +4787,7 @@ async fn turn_event_counts_completed_tool_items() {
|
|||||||
revised_prompt: None,
|
revised_prompt: None,
|
||||||
result: "ok".to_string(),
|
result: "ok".to_string(),
|
||||||
transparent_background: None,
|
transparent_background: None,
|
||||||
|
failure: None,
|
||||||
saved_path: None,
|
saved_path: None,
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -2286,6 +2286,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"ItemCompletedNotification": {
|
"ItemCompletedNotification": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"completedAtMs": {
|
"completedAtMs": {
|
||||||
@@ -4706,6 +4737,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
+42
@@ -12159,6 +12159,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"InputModality": {
|
"InputModality": {
|
||||||
"description": "Canonical user-input modality tags advertised by a model.",
|
"description": "Canonical user-input modality tags advertised by a model.",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
@@ -19950,6 +19981,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/v2/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
+42
@@ -8435,6 +8435,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"InitializeCapabilities": {
|
"InitializeCapabilities": {
|
||||||
"description": "Client-declared capabilities negotiated during initialize.",
|
"description": "Client-declared capabilities negotiated during initialize.",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -17709,6 +17740,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -314,6 +314,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"LegacyAppPathString": {
|
"LegacyAppPathString": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1216,6 +1247,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -314,6 +314,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"LegacyAppPathString": {
|
"LegacyAppPathString": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1216,6 +1247,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -452,6 +452,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"LegacyAppPathString": {
|
"LegacyAppPathString": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1361,6 +1392,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -556,6 +556,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"LegacyAppPathString": {
|
"LegacyAppPathString": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1910,6 +1941,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -478,6 +478,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"LegacyAppPathString": {
|
"LegacyAppPathString": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1701,6 +1732,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
+42
@@ -478,6 +478,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"LegacyAppPathString": {
|
"LegacyAppPathString": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1701,6 +1732,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -478,6 +478,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"LegacyAppPathString": {
|
"LegacyAppPathString": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1701,6 +1732,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -556,6 +556,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"LegacyAppPathString": {
|
"LegacyAppPathString": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1910,6 +1941,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -478,6 +478,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"LegacyAppPathString": {
|
"LegacyAppPathString": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1701,6 +1732,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -556,6 +556,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"LegacyAppPathString": {
|
"LegacyAppPathString": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1910,6 +1941,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -478,6 +478,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"LegacyAppPathString": {
|
"LegacyAppPathString": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1701,6 +1732,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -478,6 +478,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"LegacyAppPathString": {
|
"LegacyAppPathString": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1701,6 +1732,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -452,6 +452,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"LegacyAppPathString": {
|
"LegacyAppPathString": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1361,6 +1392,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -452,6 +452,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"LegacyAppPathString": {
|
"LegacyAppPathString": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1361,6 +1392,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -452,6 +452,37 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ImageGenerationFailure": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"limitId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"resetsAt": {
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"usageLimitExceeded"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailureType",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"limitId",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "UsageLimitExceededImageGenerationFailure",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"LegacyAppPathString": {
|
"LegacyAppPathString": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1361,6 +1392,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"failure": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ImageGenerationFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,5 @@
|
|||||||
|
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||||
|
|
||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
export type ImageGenerationFailure = { "type": "usageLimitExceeded", limitId: string, resetsAt: number | null, };
|
||||||
@@ -2,5 +2,6 @@
|
|||||||
|
|
||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
import type { AbsolutePathBuf } from "./AbsolutePathBuf";
|
import type { AbsolutePathBuf } from "./AbsolutePathBuf";
|
||||||
|
import type { ImageGenerationFailure } from "./ImageGenerationFailure";
|
||||||
|
|
||||||
export type ImageGenerationItem = { id: string, status: string, revisedPrompt: string | null, result: string, transparentBackground?: boolean, savedPath?: AbsolutePathBuf, };
|
export type ImageGenerationItem = { id: string, status: string, revisedPrompt: string | null, result: string, transparentBackground?: boolean, failure: ImageGenerationFailure | null, savedPath?: AbsolutePathBuf, };
|
||||||
@@ -37,6 +37,7 @@ export type { GitDiffToRemoteParams } from "./GitDiffToRemoteParams";
|
|||||||
export type { GitDiffToRemoteResponse } from "./GitDiffToRemoteResponse";
|
export type { GitDiffToRemoteResponse } from "./GitDiffToRemoteResponse";
|
||||||
export type { GitSha } from "./GitSha";
|
export type { GitSha } from "./GitSha";
|
||||||
export type { ImageDetail } from "./ImageDetail";
|
export type { ImageDetail } from "./ImageDetail";
|
||||||
|
export type { ImageGenerationFailure } from "./ImageGenerationFailure";
|
||||||
export type { ImageGenerationItem } from "./ImageGenerationItem";
|
export type { ImageGenerationItem } from "./ImageGenerationItem";
|
||||||
export type { InitializeCapabilities } from "./InitializeCapabilities";
|
export type { InitializeCapabilities } from "./InitializeCapabilities";
|
||||||
export type { InitializeParams } from "./InitializeParams";
|
export type { InitializeParams } from "./InitializeParams";
|
||||||
|
|||||||
@@ -863,6 +863,7 @@ impl ThreadHistoryBuilder {
|
|||||||
revised_prompt: None,
|
revised_prompt: None,
|
||||||
result: String::new(),
|
result: String::new(),
|
||||||
transparent_background: None,
|
transparent_background: None,
|
||||||
|
failure: None,
|
||||||
saved_path: None,
|
saved_path: None,
|
||||||
});
|
});
|
||||||
self.upsert_item_in_current_turn(item);
|
self.upsert_item_in_current_turn(item);
|
||||||
@@ -875,6 +876,7 @@ impl ThreadHistoryBuilder {
|
|||||||
revised_prompt: payload.revised_prompt.clone(),
|
revised_prompt: payload.revised_prompt.clone(),
|
||||||
result: payload.result.clone(),
|
result: payload.result.clone(),
|
||||||
transparent_background: payload.transparent_background,
|
transparent_background: payload.transparent_background,
|
||||||
|
failure: payload.failure.clone(),
|
||||||
saved_path: payload.saved_path.clone(),
|
saved_path: payload.saved_path.clone(),
|
||||||
});
|
});
|
||||||
self.upsert_item_in_current_turn(item);
|
self.upsert_item_in_current_turn(item);
|
||||||
@@ -2079,6 +2081,7 @@ mod tests {
|
|||||||
revised_prompt: Some("A blue square".to_string()),
|
revised_prompt: Some("A blue square".to_string()),
|
||||||
result: "cG5n".to_string(),
|
result: "cG5n".to_string(),
|
||||||
transparent_background: Some(true),
|
transparent_background: Some(true),
|
||||||
|
failure: None,
|
||||||
saved_path: Some(saved_path.clone()),
|
saved_path: Some(saved_path.clone()),
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
@@ -2110,6 +2113,7 @@ mod tests {
|
|||||||
revised_prompt: Some("A blue square".to_string()),
|
revised_prompt: Some("A blue square".to_string()),
|
||||||
result: "cG5n".to_string(),
|
result: "cG5n".to_string(),
|
||||||
transparent_background: Some(true),
|
transparent_background: Some(true),
|
||||||
|
failure: None,
|
||||||
saved_path: Some(saved_path),
|
saved_path: Some(saved_path),
|
||||||
})]
|
})]
|
||||||
);
|
);
|
||||||
@@ -2373,6 +2377,12 @@ mod tests {
|
|||||||
revised_prompt: Some("final prompt".into()),
|
revised_prompt: Some("final prompt".into()),
|
||||||
result: "Zm9v".into(),
|
result: "Zm9v".into(),
|
||||||
transparent_background: Some(true),
|
transparent_background: Some(true),
|
||||||
|
failure: Some(
|
||||||
|
codex_extension_items::image_generation::ImageGenerationFailure::UsageLimitExceeded {
|
||||||
|
limit_id: "image_gen".into(),
|
||||||
|
resets_at: Some(1_786_150_800),
|
||||||
|
},
|
||||||
|
),
|
||||||
saved_path: Some(test_path_buf("/tmp/ig_123.png").abs()),
|
saved_path: Some(test_path_buf("/tmp/ig_123.png").abs()),
|
||||||
})),
|
})),
|
||||||
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
|
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
|
||||||
@@ -2413,6 +2423,12 @@ mod tests {
|
|||||||
revised_prompt: Some("final prompt".into()),
|
revised_prompt: Some("final prompt".into()),
|
||||||
result: "Zm9v".into(),
|
result: "Zm9v".into(),
|
||||||
transparent_background: Some(true),
|
transparent_background: Some(true),
|
||||||
|
failure: Some(
|
||||||
|
codex_extension_items::image_generation::ImageGenerationFailure::UsageLimitExceeded {
|
||||||
|
limit_id: "image_gen".into(),
|
||||||
|
resets_at: Some(1_786_150_800),
|
||||||
|
},
|
||||||
|
),
|
||||||
saved_path: Some(test_path_buf("/tmp/ig_123.png").abs()),
|
saved_path: Some(test_path_buf("/tmp/ig_123.png").abs()),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use crate::protocol::item_builders::convert_patch_changes;
|
|||||||
use crate::protocol::item_builders::review_output_text;
|
use crate::protocol::item_builders::review_output_text;
|
||||||
use codex_experimental_api_macros::ExperimentalApi;
|
use codex_experimental_api_macros::ExperimentalApi;
|
||||||
use codex_extension_items::ExtensionItem;
|
use codex_extension_items::ExtensionItem;
|
||||||
|
pub use codex_extension_items::image_generation::ImageGenerationFailure;
|
||||||
pub use codex_extension_items::image_generation::ImageGenerationItem;
|
pub use codex_extension_items::image_generation::ImageGenerationItem;
|
||||||
pub use codex_extension_items::sleep::SleepItem;
|
pub use codex_extension_items::sleep::SleepItem;
|
||||||
pub use codex_extension_items::web_search::WebSearchAction;
|
pub use codex_extension_items::web_search::WebSearchAction;
|
||||||
@@ -929,6 +930,7 @@ impl From<CoreTurnItem> for ThreadItem {
|
|||||||
revised_prompt: image.revised_prompt,
|
revised_prompt: image.revised_prompt,
|
||||||
result: image.result,
|
result: image.result,
|
||||||
transparent_background: None,
|
transparent_background: None,
|
||||||
|
failure: None,
|
||||||
saved_path: image.saved_path,
|
saved_path: image.saved_path,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ mod tests {
|
|||||||
revised_prompt: Some("revised".to_string()),
|
revised_prompt: Some("revised".to_string()),
|
||||||
result: "base64-result".to_string(),
|
result: "base64-result".to_string(),
|
||||||
transparent_background: None,
|
transparent_background: None,
|
||||||
|
failure: None,
|
||||||
saved_path: Some(test_path_buf("/tmp/ig-1.png").abs()),
|
saved_path: Some(test_path_buf("/tmp/ig-1.png").abs()),
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use app_test_support::ChatGptAuthFixture;
|
|||||||
use app_test_support::MockResponsesConfig;
|
use app_test_support::MockResponsesConfig;
|
||||||
use app_test_support::TestAppServer;
|
use app_test_support::TestAppServer;
|
||||||
use app_test_support::write_chatgpt_auth;
|
use app_test_support::write_chatgpt_auth;
|
||||||
|
use codex_app_server_protocol::ImageGenerationFailure;
|
||||||
use codex_app_server_protocol::ImageGenerationItem;
|
use codex_app_server_protocol::ImageGenerationItem;
|
||||||
use codex_app_server_protocol::ItemCompletedNotification;
|
use codex_app_server_protocol::ItemCompletedNotification;
|
||||||
use codex_app_server_protocol::ThreadItem;
|
use codex_app_server_protocol::ThreadItem;
|
||||||
@@ -416,6 +417,7 @@ async fn standalone_image_generation_failure_emits_terminal_item() -> Result<()>
|
|||||||
revised_prompt: Some("paint a blue whale".to_string()),
|
revised_prompt: Some("paint a blue whale".to_string()),
|
||||||
result: String::new(),
|
result: String::new(),
|
||||||
transparent_background: None,
|
transparent_background: None,
|
||||||
|
failure: None,
|
||||||
saved_path: None,
|
saved_path: None,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -430,10 +432,145 @@ async fn standalone_image_generation_failure_emits_terminal_item() -> Result<()>
|
|||||||
let (output, _) = requests[1]
|
let (output, _) = requests[1]
|
||||||
.function_call_output_content_and_success(call_id)
|
.function_call_output_content_and_success(call_id)
|
||||||
.context("image generation function output should be present")?;
|
.context("image generation function output should be present")?;
|
||||||
assert!(
|
assert_eq!(
|
||||||
output
|
output.as_deref(),
|
||||||
.as_deref()
|
Some(
|
||||||
.is_some_and(|text| text.contains("image generation failed"))
|
"image generation failed: http 500 Internal Server Error: Some(\"image backend failed\")"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn image_generation_usage_limit_preserves_correlated_failure_metadata() -> Result<()> {
|
||||||
|
let call_id = "image-run-limited";
|
||||||
|
let reset_at = 1_786_150_800;
|
||||||
|
let server = responses::start_mock_server().await;
|
||||||
|
Mock::given(method("POST"))
|
||||||
|
.and(path("/api/codex/images/generations"))
|
||||||
|
.respond_with(
|
||||||
|
ResponseTemplate::new(429)
|
||||||
|
.insert_header("x-codex-active-limit", "image_gen")
|
||||||
|
.insert_header("x-image-gen-primary-used-percent", "100")
|
||||||
|
.insert_header("x-image-gen-primary-window-minutes", "1440")
|
||||||
|
.insert_header("x-image-gen-primary-reset-at", reset_at.to_string())
|
||||||
|
.set_body_json(json!({
|
||||||
|
"error": {
|
||||||
|
"type": "usage_limit_reached",
|
||||||
|
"message": "image limit reached",
|
||||||
|
"resets_at": reset_at,
|
||||||
|
"plan_type": "plus"
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.expect(1)
|
||||||
|
.mount(&server)
|
||||||
|
.await;
|
||||||
|
responses::mount_sse_sequence(
|
||||||
|
&server,
|
||||||
|
vec![
|
||||||
|
responses::sse(vec![
|
||||||
|
responses::ev_response_created("resp-1"),
|
||||||
|
responses::ev_function_call_with_namespace(
|
||||||
|
call_id,
|
||||||
|
"image_gen",
|
||||||
|
"imagegen",
|
||||||
|
&json!({"prompt": "paint a blue whale"}).to_string(),
|
||||||
|
),
|
||||||
|
responses::ev_completed("resp-1"),
|
||||||
|
]),
|
||||||
|
responses::sse(vec![
|
||||||
|
responses::ev_assistant_message("msg-1", "The image limit was reached."),
|
||||||
|
responses::ev_completed("resp-2"),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let codex_home = TempDir::new()?;
|
||||||
|
create_config_toml(codex_home.path(), &server.uri(), ImagegenTestMode::Direct)?;
|
||||||
|
write_chatgpt_auth(
|
||||||
|
codex_home.path(),
|
||||||
|
ChatGptAuthFixture::new("access-chatgpt"),
|
||||||
|
AuthCredentialsStoreMode::File,
|
||||||
|
)?;
|
||||||
|
let mut mcp = TestAppServer::builder()
|
||||||
|
.with_codex_home(codex_home.path())
|
||||||
|
.with_env_overrides(&[("OPENAI_API_KEY", None)])
|
||||||
|
.build_initialized_with_timeout(DEFAULT_READ_TIMEOUT)
|
||||||
|
.await?;
|
||||||
|
start_image_generation_turn(&mut mcp, ThreadStartParams::default()).await?;
|
||||||
|
|
||||||
|
let completed = timeout(
|
||||||
|
DEFAULT_READ_TIMEOUT,
|
||||||
|
wait_for_image_generation_completed(&mut mcp),
|
||||||
|
)
|
||||||
|
.await??;
|
||||||
|
let thread_id = completed.thread_id.clone();
|
||||||
|
let ThreadItem::ImageGeneration(image) = completed.item else {
|
||||||
|
panic!("expected failed image-generation item");
|
||||||
|
};
|
||||||
|
assert_eq!(image.status, "failed");
|
||||||
|
assert_eq!(
|
||||||
|
image.failure,
|
||||||
|
Some(ImageGenerationFailure::UsageLimitExceeded {
|
||||||
|
limit_id: "image_gen".to_string(),
|
||||||
|
resets_at: Some(reset_at),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
timeout(
|
||||||
|
DEFAULT_READ_TIMEOUT,
|
||||||
|
mcp.read_stream_until_notification_message("turn/completed"),
|
||||||
|
)
|
||||||
|
.await??;
|
||||||
|
let read_id = mcp
|
||||||
|
.send_thread_read_request(ThreadReadParams {
|
||||||
|
thread_id: thread_id.clone(),
|
||||||
|
include_turns: true,
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
let ThreadReadResponse { thread, .. } =
|
||||||
|
timeout(DEFAULT_READ_TIMEOUT, mcp.read_response(read_id)).await??;
|
||||||
|
let persisted_failure = thread
|
||||||
|
.turns
|
||||||
|
.iter()
|
||||||
|
.flat_map(|turn| turn.items.iter())
|
||||||
|
.find_map(|item| match item {
|
||||||
|
ThreadItem::ImageGeneration(item) => item.failure.as_ref(),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
persisted_failure,
|
||||||
|
Some(&ImageGenerationFailure::UsageLimitExceeded {
|
||||||
|
limit_id: "image_gen".to_string(),
|
||||||
|
resets_at: Some(reset_at),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let resume_id = mcp
|
||||||
|
.send_thread_resume_request(ThreadResumeParams {
|
||||||
|
thread_id,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
let ThreadResumeResponse { thread, .. } =
|
||||||
|
timeout(DEFAULT_READ_TIMEOUT, mcp.read_response(resume_id)).await??;
|
||||||
|
let resumed_failure = thread
|
||||||
|
.turns
|
||||||
|
.iter()
|
||||||
|
.flat_map(|turn| turn.items.iter())
|
||||||
|
.find_map(|item| match item {
|
||||||
|
ThreadItem::ImageGeneration(item) => item.failure.as_ref(),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
resumed_failure,
|
||||||
|
Some(&ImageGenerationFailure::UsageLimitExceeded {
|
||||||
|
limit_id: "image_gen".to_string(),
|
||||||
|
resets_at: Some(reset_at),
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1844,6 +1844,7 @@ fn append_resume_redaction_history(
|
|||||||
revised_prompt: Some("secret revised prompt".to_string()),
|
revised_prompt: Some("secret revised prompt".to_string()),
|
||||||
result: "base64-image-result".to_string(),
|
result: "base64-image-result".to_string(),
|
||||||
transparent_background: None,
|
transparent_background: None,
|
||||||
|
failure: None,
|
||||||
saved_path: Some(test_absolute_path("/tmp/ig-1.png")),
|
saved_path: Some(test_absolute_path("/tmp/ig-1.png")),
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -473,6 +473,7 @@ mod tests {
|
|||||||
revised_prompt: None,
|
revised_prompt: None,
|
||||||
result: String::new(),
|
result: String::new(),
|
||||||
transparent_background: None,
|
transparent_background: None,
|
||||||
|
failure: None,
|
||||||
saved_path: None,
|
saved_path: None,
|
||||||
});
|
});
|
||||||
let expected_completed_item = ExtensionItem::ImageGeneration(ImageGenerationItem {
|
let expected_completed_item = ExtensionItem::ImageGeneration(ImageGenerationItem {
|
||||||
@@ -481,6 +482,7 @@ mod tests {
|
|||||||
revised_prompt: Some("A tiny blue square".to_string()),
|
revised_prompt: Some("A tiny blue square".to_string()),
|
||||||
result: "cG5n".to_string(),
|
result: "cG5n".to_string(),
|
||||||
transparent_background: Some(true),
|
transparent_background: Some(true),
|
||||||
|
failure: None,
|
||||||
saved_path: Some(expected_path.clone()),
|
saved_path: Some(expected_path.clone()),
|
||||||
});
|
});
|
||||||
codex_tools::TurnItemEmitter::emit_started(
|
codex_tools::TurnItemEmitter::emit_started(
|
||||||
@@ -503,6 +505,7 @@ mod tests {
|
|||||||
revised_prompt: Some("A tiny blue square".to_string()),
|
revised_prompt: Some("A tiny blue square".to_string()),
|
||||||
result: "cG5n".to_string(),
|
result: "cG5n".to_string(),
|
||||||
transparent_background: Some(true),
|
transparent_background: Some(true),
|
||||||
|
failure: None,
|
||||||
saved_path: Some(expected_path.clone()),
|
saved_path: Some(expected_path.clone()),
|
||||||
})],
|
})],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,14 +3,46 @@ use codex_api::ImageGenerationRequest;
|
|||||||
use codex_api::ImageResponse;
|
use codex_api::ImageResponse;
|
||||||
use codex_api::ImagesClient;
|
use codex_api::ImagesClient;
|
||||||
use codex_api::ReqwestTransport;
|
use codex_api::ReqwestTransport;
|
||||||
|
use codex_api::map_api_error;
|
||||||
use codex_login::default_client::add_originator_header;
|
use codex_login::default_client::add_originator_header;
|
||||||
use codex_login::default_client::create_client;
|
use codex_login::default_client::create_client;
|
||||||
use codex_model_provider::SharedModelProvider;
|
use codex_model_provider::SharedModelProvider;
|
||||||
|
use codex_protocol::error::CodexErr;
|
||||||
use http::HeaderMap;
|
use http::HeaderMap;
|
||||||
use http::HeaderValue;
|
use http::HeaderValue;
|
||||||
|
|
||||||
const X_CODEX_IMAGE_TURN_ID_HEADER: &str = "x-codex-image-turn-id";
|
const X_CODEX_IMAGE_TURN_ID_HEADER: &str = "x-codex-image-turn-id";
|
||||||
|
|
||||||
|
pub(crate) struct ImageBackendError {
|
||||||
|
message: String,
|
||||||
|
codex_error: CodexErr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageBackendError {
|
||||||
|
fn from_api(error: codex_api::ApiError) -> Self {
|
||||||
|
let message = error.to_string();
|
||||||
|
Self {
|
||||||
|
message,
|
||||||
|
codex_error: map_api_error(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_message(message: String) -> Self {
|
||||||
|
Self {
|
||||||
|
codex_error: CodexErr::Stream(message.clone()),
|
||||||
|
message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn message(&self) -> &str {
|
||||||
|
&self.message
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn codex_error(&self) -> &CodexErr {
|
||||||
|
&self.codex_error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct CodexImagesBackend {
|
pub(crate) struct CodexImagesBackend {
|
||||||
provider: SharedModelProvider,
|
provider: SharedModelProvider,
|
||||||
@@ -27,17 +59,17 @@ impl CodexImagesBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves the provider and auth required for the current image API request.
|
/// Resolves the provider and auth required for the current image API request.
|
||||||
async fn client(&self) -> Result<ImagesClient<ReqwestTransport>, String> {
|
async fn client(&self) -> Result<ImagesClient<ReqwestTransport>, ImageBackendError> {
|
||||||
let provider = self
|
let provider = self
|
||||||
.provider
|
.provider
|
||||||
.api_provider()
|
.api_provider()
|
||||||
.await
|
.await
|
||||||
.map_err(|err| err.to_string())?;
|
.map_err(|err| ImageBackendError::from_message(err.to_string()))?;
|
||||||
let auth = self
|
let auth = self
|
||||||
.provider
|
.provider
|
||||||
.api_auth()
|
.api_auth()
|
||||||
.await
|
.await
|
||||||
.map_err(|err| err.to_string())?;
|
.map_err(|err| ImageBackendError::from_message(err.to_string()))?;
|
||||||
Ok(ImagesClient::new(
|
Ok(ImagesClient::new(
|
||||||
ReqwestTransport::from_http_client(create_client()),
|
ReqwestTransport::from_http_client(create_client()),
|
||||||
provider,
|
provider,
|
||||||
@@ -50,7 +82,7 @@ impl CodexImagesBackend {
|
|||||||
&self,
|
&self,
|
||||||
request: ImageGenerationRequest,
|
request: ImageGenerationRequest,
|
||||||
turn_id: &str,
|
turn_id: &str,
|
||||||
) -> Result<ImageResponse, String> {
|
) -> Result<ImageResponse, ImageBackendError> {
|
||||||
self.client()
|
self.client()
|
||||||
.await?
|
.await?
|
||||||
.generate(
|
.generate(
|
||||||
@@ -58,7 +90,7 @@ impl CodexImagesBackend {
|
|||||||
image_request_headers(self.originator.as_deref(), turn_id),
|
image_request_headers(self.originator.as_deref(), turn_id),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| err.to_string())
|
.map_err(ImageBackendError::from_api)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a standalone image edit request through the configured Images client.
|
/// Sends a standalone image edit request through the configured Images client.
|
||||||
@@ -66,7 +98,7 @@ impl CodexImagesBackend {
|
|||||||
&self,
|
&self,
|
||||||
request: ImageEditRequest,
|
request: ImageEditRequest,
|
||||||
turn_id: &str,
|
turn_id: &str,
|
||||||
) -> Result<ImageResponse, String> {
|
) -> Result<ImageResponse, ImageBackendError> {
|
||||||
self.client()
|
self.client()
|
||||||
.await?
|
.await?
|
||||||
.edit(
|
.edit(
|
||||||
@@ -74,7 +106,7 @@ impl CodexImagesBackend {
|
|||||||
image_request_headers(self.originator.as_deref(), turn_id),
|
image_request_headers(self.originator.as_deref(), turn_id),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| err.to_string())
|
.map_err(ImageBackendError::from_api)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,10 @@ use codex_extension_api::ToolPayload;
|
|||||||
use codex_extension_api::ToolSpec;
|
use codex_extension_api::ToolSpec;
|
||||||
use codex_extension_api::parse_tool_input_schema;
|
use codex_extension_api::parse_tool_input_schema;
|
||||||
use codex_extension_items::ExtensionItem;
|
use codex_extension_items::ExtensionItem;
|
||||||
|
use codex_extension_items::image_generation::ImageGenerationFailure;
|
||||||
use codex_extension_items::image_generation::ImageGenerationItem;
|
use codex_extension_items::image_generation::ImageGenerationItem;
|
||||||
|
use codex_protocol::error::CodexErr;
|
||||||
|
use codex_protocol::error::CodexErrorDetails;
|
||||||
use codex_protocol::models::ContentItem;
|
use codex_protocol::models::ContentItem;
|
||||||
use codex_protocol::models::DEFAULT_IMAGE_DETAIL;
|
use codex_protocol::models::DEFAULT_IMAGE_DETAIL;
|
||||||
use codex_protocol::models::FunctionCallOutputBody;
|
use codex_protocol::models::FunctionCallOutputBody;
|
||||||
@@ -97,6 +100,7 @@ fn legacy_end_event(item: &ImageGenerationItem) -> EventMsg {
|
|||||||
revised_prompt: item.revised_prompt.clone(),
|
revised_prompt: item.revised_prompt.clone(),
|
||||||
result: item.result.clone(),
|
result: item.result.clone(),
|
||||||
transparent_background: item.transparent_background,
|
transparent_background: item.transparent_background,
|
||||||
|
failure: item.failure.clone(),
|
||||||
saved_path: item.saved_path.clone(),
|
saved_path: item.saved_path.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -144,6 +148,7 @@ impl ImageGenerationTool {
|
|||||||
revised_prompt: None,
|
revised_prompt: None,
|
||||||
result: String::new(),
|
result: String::new(),
|
||||||
transparent_background: None,
|
transparent_background: None,
|
||||||
|
failure: None,
|
||||||
saved_path: None,
|
saved_path: None,
|
||||||
},
|
},
|
||||||
EventMsg::ImageGenerationBegin(ImageGenerationBeginEvent {
|
EventMsg::ImageGenerationBegin(ImageGenerationBeginEvent {
|
||||||
@@ -155,7 +160,12 @@ impl ImageGenerationTool {
|
|||||||
ImageRequest::Generate(request) => self.backend.generate(request, &call.turn_id).await,
|
ImageRequest::Generate(request) => self.backend.generate(request, &call.turn_id).await,
|
||||||
ImageRequest::Edit(request) => self.backend.edit(request, &call.turn_id).await,
|
ImageRequest::Edit(request) => self.backend.edit(request, &call.turn_id).await,
|
||||||
}
|
}
|
||||||
.map_err(|err| format!("image generation failed: {err}"))
|
.map_err(|error| {
|
||||||
|
(
|
||||||
|
format!("image generation failed: {}", error.message()),
|
||||||
|
usage_limit_failure(error.codex_error()),
|
||||||
|
)
|
||||||
|
})
|
||||||
.and_then(|response| {
|
.and_then(|response| {
|
||||||
let transparent_background = match response.background {
|
let transparent_background = match response.background {
|
||||||
Some(ImageBackground::Transparent) => Some(true),
|
Some(ImageBackground::Transparent) => Some(true),
|
||||||
@@ -167,17 +177,18 @@ impl ImageGenerationTool {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.next()
|
.next()
|
||||||
.map(|data| (data.b64_json, transparent_background))
|
.map(|data| (data.b64_json, transparent_background))
|
||||||
.ok_or_else(|| "image generation returned no image data".to_string())
|
.ok_or_else(|| ("image generation returned no image data".to_string(), None))
|
||||||
});
|
});
|
||||||
let (result, transparent_background) = match result {
|
let (result, transparent_background) = match result {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(message) => {
|
Err((message, failure)) => {
|
||||||
let item = ImageGenerationItem {
|
let item = ImageGenerationItem {
|
||||||
id: call.call_id.clone(),
|
id: call.call_id.clone(),
|
||||||
status: "failed".to_string(),
|
status: "failed".to_string(),
|
||||||
revised_prompt: Some(args.prompt),
|
revised_prompt: Some(args.prompt),
|
||||||
result: String::new(),
|
result: String::new(),
|
||||||
transparent_background: None,
|
transparent_background: None,
|
||||||
|
failure,
|
||||||
saved_path: None,
|
saved_path: None,
|
||||||
};
|
};
|
||||||
let legacy_event = legacy_end_event(&item);
|
let legacy_event = legacy_end_event(&item);
|
||||||
@@ -218,6 +229,7 @@ impl ImageGenerationTool {
|
|||||||
revised_prompt: Some(args.prompt),
|
revised_prompt: Some(args.prompt),
|
||||||
result: result.clone(),
|
result: result.clone(),
|
||||||
transparent_background,
|
transparent_background,
|
||||||
|
failure: None,
|
||||||
saved_path: saved_path.clone(),
|
saved_path: saved_path.clone(),
|
||||||
};
|
};
|
||||||
let legacy_event = legacy_end_event(&item);
|
let legacy_event = legacy_end_event(&item);
|
||||||
@@ -235,6 +247,33 @@ impl ImageGenerationTool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn usage_limit_failure(error: &CodexErr) -> Option<ImageGenerationFailure> {
|
||||||
|
let CodexErrorDetails::UsageLimitReached(usage_limit) = error.details() else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let rate_limits = usage_limit.rate_limits.as_deref()?;
|
||||||
|
let limit_id = rate_limits.limit_id.as_deref()?;
|
||||||
|
if limit_id != "image_gen" {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let resets_at = if let Some(reset_at) = usage_limit.resets_at.as_ref() {
|
||||||
|
Some(reset_at.timestamp())
|
||||||
|
} else {
|
||||||
|
[rate_limits.primary.as_ref(), rate_limits.secondary.as_ref()]
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.filter(|window| window.used_percent >= 100.0)
|
||||||
|
.filter_map(|window| window.resets_at)
|
||||||
|
.max()
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(ImageGenerationFailure::UsageLimitExceeded {
|
||||||
|
limit_id: limit_id.to_string(),
|
||||||
|
resets_at,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async fn save_image_generation_result(
|
async fn save_image_generation_result(
|
||||||
fs: &dyn ExecutorFileSystem,
|
fs: &dyn ExecutorFileSystem,
|
||||||
save_root: &AbsolutePathBuf,
|
save_root: &AbsolutePathBuf,
|
||||||
|
|||||||
@@ -4,6 +4,21 @@ use serde::Deserialize;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema, PartialEq)]
|
||||||
|
#[serde(tag = "type", rename_all = "camelCase")]
|
||||||
|
#[ts(tag = "type", rename_all = "camelCase")]
|
||||||
|
pub enum ImageGenerationFailure {
|
||||||
|
UsageLimitExceeded {
|
||||||
|
#[serde(rename = "limitId")]
|
||||||
|
#[ts(rename = "limitId")]
|
||||||
|
limit_id: String,
|
||||||
|
#[serde(rename = "resetsAt")]
|
||||||
|
#[ts(rename = "resetsAt")]
|
||||||
|
#[ts(type = "number | null")]
|
||||||
|
resets_at: Option<i64>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// Standalone image-generation item owned by the image extension. This is also
|
// Standalone image-generation item owned by the image extension. This is also
|
||||||
// the field-level representation exposed by app-server; core and rollout
|
// the field-level representation exposed by app-server; core and rollout
|
||||||
// persistence only carry it inside an ExtensionItem envelope.
|
// persistence only carry it inside an ExtensionItem envelope.
|
||||||
@@ -18,6 +33,8 @@ pub struct ImageGenerationItem {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
pub transparent_background: Option<bool>,
|
pub transparent_background: Option<bool>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub failure: Option<ImageGenerationFailure>,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
pub saved_path: Option<AbsolutePathBuf>,
|
pub saved_path: Option<AbsolutePathBuf>,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ fn completed_image_generation_item() -> ExtensionItem {
|
|||||||
revised_prompt: Some("A blue square".to_string()),
|
revised_prompt: Some("A blue square".to_string()),
|
||||||
result: "cG5n".to_string(),
|
result: "cG5n".to_string(),
|
||||||
transparent_background: None,
|
transparent_background: None,
|
||||||
|
failure: None,
|
||||||
saved_path: None,
|
saved_path: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -33,6 +34,7 @@ fn image_generation_item_preserves_stable_wire_shape() {
|
|||||||
"revisedPrompt": "A blue square",
|
"revisedPrompt": "A blue square",
|
||||||
"result": "cG5n",
|
"result": "cG5n",
|
||||||
"transparentBackground": null,
|
"transparentBackground": null,
|
||||||
|
"failure": null,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -70,6 +72,7 @@ fn image_generation_item_preserves_authoritative_transparency() {
|
|||||||
"revisedPrompt": "A blue square",
|
"revisedPrompt": "A blue square",
|
||||||
"result": "cG5n",
|
"result": "cG5n",
|
||||||
"transparentBackground": true,
|
"transparentBackground": true,
|
||||||
|
"failure": null,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -430,6 +430,7 @@ impl ImageGenerationItem {
|
|||||||
revised_prompt: self.revised_prompt.clone(),
|
revised_prompt: self.revised_prompt.clone(),
|
||||||
result: self.result.clone(),
|
result: self.result.clone(),
|
||||||
transparent_background: None,
|
transparent_background: None,
|
||||||
|
failure: None,
|
||||||
saved_path: self.saved_path.clone(),
|
saved_path: self.saved_path.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ use crate::request_permissions::RequestPermissionsEvent;
|
|||||||
use crate::request_permissions::RequestPermissionsResponse;
|
use crate::request_permissions::RequestPermissionsResponse;
|
||||||
use crate::request_user_input::RequestUserInputResponse;
|
use crate::request_user_input::RequestUserInputResponse;
|
||||||
use crate::user_input::UserInput;
|
use crate::user_input::UserInput;
|
||||||
|
use codex_extension_items::image_generation::ImageGenerationFailure;
|
||||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||||
use codex_utils_path_uri::PathUri;
|
use codex_utils_path_uri::PathUri;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
@@ -2549,6 +2550,9 @@ pub struct ImageGenerationEndEvent {
|
|||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
pub transparent_background: Option<bool>,
|
pub transparent_background: Option<bool>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
#[ts(optional)]
|
||||||
|
pub failure: Option<ImageGenerationFailure>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
pub saved_path: Option<AbsolutePathBuf>,
|
pub saved_path: Option<AbsolutePathBuf>,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { workspace = true, features = ["serde"] }
|
chrono = { workspace = true, features = ["serde"] }
|
||||||
codex-app-server-protocol = { workspace = true }
|
codex-app-server-protocol = { workspace = true }
|
||||||
|
codex-extension-items = { workspace = true }
|
||||||
codex-git-utils = { workspace = true }
|
codex-git-utils = { workspace = true }
|
||||||
codex-install-context = { workspace = true }
|
codex-install-context = { workspace = true }
|
||||||
codex-otel = { workspace = true }
|
codex-otel = { workspace = true }
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
//! It intentionally stays separate from live thread-history reduction because migration needs a
|
//! It intentionally stays separate from live thread-history reduction because migration needs a
|
||||||
//! frozen adapter for historical rollout payloads.
|
//! frozen adapter for historical rollout payloads.
|
||||||
|
|
||||||
|
use codex_extension_items::ExtensionItem;
|
||||||
|
use codex_extension_items::image_generation::ImageGenerationItem;
|
||||||
use codex_protocol::items::AgentMessageContent;
|
use codex_protocol::items::AgentMessageContent;
|
||||||
use codex_protocol::items::AgentMessageItem;
|
use codex_protocol::items::AgentMessageItem;
|
||||||
use codex_protocol::items::CommandExecutionItem;
|
use codex_protocol::items::CommandExecutionItem;
|
||||||
@@ -17,7 +19,6 @@ use codex_protocol::items::DynamicToolCallStatus;
|
|||||||
use codex_protocol::items::EnteredReviewModeItem;
|
use codex_protocol::items::EnteredReviewModeItem;
|
||||||
use codex_protocol::items::ExitedReviewModeItem;
|
use codex_protocol::items::ExitedReviewModeItem;
|
||||||
use codex_protocol::items::FileChangeItem;
|
use codex_protocol::items::FileChangeItem;
|
||||||
use codex_protocol::items::ImageGenerationItem;
|
|
||||||
use codex_protocol::items::McpToolCallError;
|
use codex_protocol::items::McpToolCallError;
|
||||||
use codex_protocol::items::McpToolCallItem;
|
use codex_protocol::items::McpToolCallItem;
|
||||||
use codex_protocol::items::McpToolCallStatus;
|
use codex_protocol::items::McpToolCallStatus;
|
||||||
@@ -156,13 +157,15 @@ pub(super) fn completed_item(
|
|||||||
None,
|
None,
|
||||||
)),
|
)),
|
||||||
EventMsg::ImageGenerationEnd(event) => Some((
|
EventMsg::ImageGenerationEnd(event) => Some((
|
||||||
TurnItem::ImageGeneration(ImageGenerationItem {
|
TurnItem::Extension(ExtensionItem::ImageGeneration(ImageGenerationItem {
|
||||||
id: event.call_id.clone(),
|
id: event.call_id.clone(),
|
||||||
status: event.status.clone(),
|
status: event.status.clone(),
|
||||||
revised_prompt: event.revised_prompt.clone(),
|
revised_prompt: event.revised_prompt.clone(),
|
||||||
result: event.result.clone(),
|
result: event.result.clone(),
|
||||||
|
transparent_background: event.transparent_background,
|
||||||
|
failure: event.failure.clone(),
|
||||||
saved_path: event.saved_path.clone(),
|
saved_path: event.saved_path.clone(),
|
||||||
}),
|
})),
|
||||||
None,
|
None,
|
||||||
)),
|
)),
|
||||||
EventMsg::ContextCompacted(_) => Some((
|
EventMsg::ContextCompacted(_) => Some((
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ use std::path::PathBuf;
|
|||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
use codex_app_server_protocol::build_turns_from_rollout_items;
|
use codex_app_server_protocol::build_turns_from_rollout_items;
|
||||||
|
use codex_extension_items::ExtensionItem;
|
||||||
|
use codex_extension_items::image_generation::ImageGenerationFailure;
|
||||||
|
use codex_extension_items::image_generation::ImageGenerationItem;
|
||||||
use codex_protocol::AgentPath;
|
use codex_protocol::AgentPath;
|
||||||
use codex_protocol::ThreadId;
|
use codex_protocol::ThreadId;
|
||||||
use codex_protocol::config_types::ReasoningSummary;
|
use codex_protocol::config_types::ReasoningSummary;
|
||||||
@@ -17,6 +20,7 @@ use codex_protocol::models::ResponseItem;
|
|||||||
use codex_protocol::protocol::AgentMessageEvent;
|
use codex_protocol::protocol::AgentMessageEvent;
|
||||||
use codex_protocol::protocol::AskForApproval;
|
use codex_protocol::protocol::AskForApproval;
|
||||||
use codex_protocol::protocol::EventMsg;
|
use codex_protocol::protocol::EventMsg;
|
||||||
|
use codex_protocol::protocol::ImageGenerationEndEvent;
|
||||||
use codex_protocol::protocol::InterAgentCommunication;
|
use codex_protocol::protocol::InterAgentCommunication;
|
||||||
use codex_protocol::protocol::ItemCompletedEvent;
|
use codex_protocol::protocol::ItemCompletedEvent;
|
||||||
use codex_protocol::protocol::SandboxPolicy;
|
use codex_protocol::protocol::SandboxPolicy;
|
||||||
@@ -397,6 +401,62 @@ async fn migration_projects_explicit_and_implicit_legacy_completed_items() {
|
|||||||
assert_eq!(reasoning["content"], json!(["raw"]));
|
assert_eq!(reasoning["content"], json!(["raw"]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn migration_preserves_image_generation_failure_metadata() {
|
||||||
|
let home = TempDir::new().expect("create Codex home");
|
||||||
|
let thread_id = ThreadId::new();
|
||||||
|
let expected_item = ImageGenerationItem {
|
||||||
|
id: "image-call".to_string(),
|
||||||
|
status: "failed".to_string(),
|
||||||
|
revised_prompt: Some("paint a blue whale".to_string()),
|
||||||
|
result: String::new(),
|
||||||
|
transparent_background: None,
|
||||||
|
failure: Some(ImageGenerationFailure::UsageLimitExceeded {
|
||||||
|
limit_id: "image_gen".to_string(),
|
||||||
|
resets_at: Some(1_786_150_800),
|
||||||
|
}),
|
||||||
|
saved_path: None,
|
||||||
|
};
|
||||||
|
let image_completion =
|
||||||
|
RolloutItem::EventMsg(EventMsg::ImageGenerationEnd(ImageGenerationEndEvent {
|
||||||
|
call_id: expected_item.id.clone(),
|
||||||
|
status: expected_item.status.clone(),
|
||||||
|
revised_prompt: expected_item.revised_prompt.clone(),
|
||||||
|
result: expected_item.result.clone(),
|
||||||
|
transparent_background: expected_item.transparent_background,
|
||||||
|
failure: expected_item.failure.clone(),
|
||||||
|
saved_path: expected_item.saved_path.clone(),
|
||||||
|
}));
|
||||||
|
let path = write_rollout(
|
||||||
|
home.path(),
|
||||||
|
thread_id,
|
||||||
|
SessionSource::Cli,
|
||||||
|
vec![
|
||||||
|
started("image-turn"),
|
||||||
|
image_completion,
|
||||||
|
completed("image-turn"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let store = indexed_store(home.path()).await;
|
||||||
|
|
||||||
|
store
|
||||||
|
.migrate_rollouts(apply_options())
|
||||||
|
.await
|
||||||
|
.expect("migrate legacy image completion");
|
||||||
|
|
||||||
|
let migrated_item = read_rollout(&path)
|
||||||
|
.into_iter()
|
||||||
|
.find_map(|line| match line.item {
|
||||||
|
RolloutItem::EventMsg(EventMsg::ItemCompleted(event)) => Some(event.item),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.expect("migrated image completion");
|
||||||
|
let TurnItem::Extension(ExtensionItem::ImageGeneration(migrated_item)) = migrated_item else {
|
||||||
|
panic!("expected migrated extension image-generation item");
|
||||||
|
};
|
||||||
|
assert_eq!(migrated_item, expected_item);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn migration_keeps_late_completions_in_their_original_turn() {
|
async fn migration_keeps_late_completions_in_their_original_turn() {
|
||||||
let home = TempDir::new().expect("create Codex home");
|
let home = TempDir::new().expect("create Codex home");
|
||||||
|
|||||||
@@ -713,6 +713,7 @@ pub(super) fn handle_image_generation_end(
|
|||||||
revised_prompt,
|
revised_prompt,
|
||||||
result: String::new(),
|
result: String::new(),
|
||||||
transparent_background: None,
|
transparent_background: None,
|
||||||
|
failure: None,
|
||||||
saved_path,
|
saved_path,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in new issue
Block a user