diff --git a/a2a-t-core/src/main/java/net/openan/a2at/sdk/core/exception/ErrorCatalog.java b/a2a-t-core/src/main/java/net/openan/a2at/sdk/core/exception/ErrorCatalog.java index f890661a..ae918c36 100644 --- a/a2a-t-core/src/main/java/net/openan/a2at/sdk/core/exception/ErrorCatalog.java +++ b/a2a-t-core/src/main/java/net/openan/a2at/sdk/core/exception/ErrorCatalog.java @@ -13,9 +13,10 @@ * Closed catalog of the machine-readable error codes exposed by the A2A-T SDK. * *

Each constant carries the layered {@code domain.semantic} code string (for example {@code content.param_missing}), - * the {@link Category} that decides which exception family carries it, and the names of the fact parameters the message - * template for the code renders. Message templates live in {@code prompt_resources/errors/{language}/errors.json}; use - * {@link ErrorMessages} to render them. + * the {@link Category} that decides which exception family carries it, and the names of the fact parameters the code + * declares. A message template may render a subset of the declared facts: {@code template.not_found} keeps + * {@code language} in its facts for programmatic diagnosis while its message only reports the missing template. Message + * templates live in {@code prompt_resources/errors/{language}/errors.json}; use {@link ErrorMessages} to render them. * *

The catalog is closed: codes returned by an LLM step that are not in this catalog are mapped to the per-domain * {@code *.rule_violation} fallback constants by the callers, never surfaced raw. @@ -26,7 +27,7 @@ public enum ErrorCatalog { // domain: template (template resources) - /** The referenced template does not exist for the configured language. */ + /** The referenced template is missing for the configured language; the message names the template URI. */ TEMPLATE_NOT_FOUND("template.not_found", Category.BUSINESS, "template_uri", "language"), /** Rendering a template with its slot values failed. */ @@ -222,7 +223,10 @@ public static Optional byCode(@NonNull String code) { } /** - * Returns the names of the fact parameters the message template of the code renders. + * Returns the names of the fact parameters the code declares. + * + *

A message template may render a subset of these facts; the remaining ones stay available for programmatic + * diagnosis. * * @return fact parameter names, possibly empty, never null */ diff --git a/a2a-t-resources/src/main/resources/prompt_resources/errors/en-US/errors.json b/a2a-t-resources/src/main/resources/prompt_resources/errors/en-US/errors.json index b90a18d2..17ee93a8 100644 --- a/a2a-t-resources/src/main/resources/prompt_resources/errors/en-US/errors.json +++ b/a2a-t-resources/src/main/resources/prompt_resources/errors/en-US/errors.json @@ -1,5 +1,5 @@ { - "template.not_found": "Template '{template_uri}' does not support language '{language}'; check the template URI and language setting", + "template.not_found": "Template '{template_uri}' does not exist", "template.render_failed": "Failed to render template '{template_uri}': {reason}", "template.load_failed": "Failed to read template resource '{resource_path}'", "slot.schema_not_found": "Template '{template_uri}' is missing its slot schema (language '{language}')", diff --git a/a2a-t-resources/src/main/resources/prompt_resources/errors/zh-CN/errors.json b/a2a-t-resources/src/main/resources/prompt_resources/errors/zh-CN/errors.json index a42f8c7d..90cce849 100644 --- a/a2a-t-resources/src/main/resources/prompt_resources/errors/zh-CN/errors.json +++ b/a2a-t-resources/src/main/resources/prompt_resources/errors/zh-CN/errors.json @@ -1,5 +1,5 @@ { - "template.not_found": "模板「{template_uri}」不支持语言「{language}」,请检查模板标识与语言配置", + "template.not_found": "模板「{template_uri}」不存在", "template.render_failed": "模板「{template_uri}」渲染失败:{reason}", "template.load_failed": "模板资源「{resource_path}」读取失败", "slot.schema_not_found": "模板「{template_uri}」缺少参数定义文件(语言「{language}」)", diff --git a/a2a-t-resources/src/test/java/net/openan/a2at/sdk/resources/ErrorMessagesResourceTest.java b/a2a-t-resources/src/test/java/net/openan/a2at/sdk/resources/ErrorMessagesResourceTest.java new file mode 100644 index 00000000..e5359b50 --- /dev/null +++ b/a2a-t-resources/src/test/java/net/openan/a2at/sdk/resources/ErrorMessagesResourceTest.java @@ -0,0 +1,58 @@ +package net.openan.a2at.sdk.resources; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import java.util.Map; +import net.openan.a2at.sdk.core.exception.ErrorCatalog; +import net.openan.a2at.sdk.core.exception.ErrorMessages; +import org.junit.jupiter.api.Test; + +/** + * Integration tests for the bundled {@code prompt_resources/errors/{language}/errors.json} resources rendered through + * {@link ErrorMessages}. + * + *

Tests cover the following scenarios: + * + *

+ * + * @since 2026-09 + */ +class ErrorMessagesResourceTest { + + private static final String UNKNOWN_URI = "Task-T/network-layer/does-not-exist/v1"; + + private static final Map FACTS = Map.of("template_uri", UNKNOWN_URI, "language", "fr-FR"); + + /** + * Verifies the bundled Chinese {@code template.not_found} message reports the missing template and does not echo + * the configured language. + * + *

Scenario: Render the catalog entry with a template URI and an unsupported language. Expected result: the + * message states the template does not exist and omits the language. + */ + @Test + void should_reportMissingTemplate_When_renderedInChinese() { + String message = ErrorMessages.render(ErrorCatalog.TEMPLATE_NOT_FOUND, "zh-CN", FACTS); + + assertEquals("模板「" + UNKNOWN_URI + "」不存在", message); + assertFalse(message.contains("fr-FR"), message); + } + + /** + * Verifies the bundled English {@code template.not_found} message reports the missing template and does not echo + * the configured language. + * + *

Scenario: Render the catalog entry with a template URI and an unsupported language. Expected result: the + * message states the template does not exist and omits the language. + */ + @Test + void should_reportMissingTemplate_When_renderedInEnglish() { + String message = ErrorMessages.render(ErrorCatalog.TEMPLATE_NOT_FOUND, "en-US", FACTS); + + assertEquals("Template '" + UNKNOWN_URI + "' does not exist", message); + assertFalse(message.contains("fr-FR"), message); + } +} diff --git a/a2a-t-server/src/test/java/net/openan/a2at/sdk/server/metadata/LlmBackedPromptMetadataExtractorTest.java b/a2a-t-server/src/test/java/net/openan/a2at/sdk/server/metadata/LlmBackedPromptMetadataExtractorTest.java index 3e1b8123..de63be6c 100644 --- a/a2a-t-server/src/test/java/net/openan/a2at/sdk/server/metadata/LlmBackedPromptMetadataExtractorTest.java +++ b/a2a-t-server/src/test/java/net/openan/a2at/sdk/server/metadata/LlmBackedPromptMetadataExtractorTest.java @@ -1,6 +1,7 @@ package net.openan.a2at.sdk.server.metadata; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -235,7 +236,11 @@ void extractPropagatesTemplateLoadErrorsAsGenerationFailures() { assertEquals("template.not_found", error.getCode()); assertEquals("generation", error.getStage()); assertTrue(error.getMessage().contains("subscribe-incident"), "message must render the template URI"); - assertTrue(error.getMessage().contains("zh-CN"), "message must render the language"); + assertFalse(error.getMessage().contains("zh-CN"), "message must not echo the configured language"); + assertEquals( + Map.of("template_uri", "subscribe-incident", "language", "zh-CN"), + error.getFacts(), + "facts must keep the language for programmatic diagnosis"); } @Test diff --git a/docs/en/API_Reference.md b/docs/en/API_Reference.md index ac7c57b1..2390fe57 100644 --- a/docs/en/API_Reference.md +++ b/docs/en/API_Reference.md @@ -1639,7 +1639,7 @@ result.failure() = | Code | Category | Message (en-US) | Message (zh-CN) | | ---- | -------- | ---------------- | --------------- | -| `template.not_found` | BUSINESS | Template '{template_uri}' does not support language '{language}'; check the template URI and language setting | 模板「{template_uri}」不支持语言「{language}」,请检查模板标识与语言配置 | +| `template.not_found` | BUSINESS | Template '{template_uri}' does not exist | 模板「{template_uri}」不存在 | | `template.render_failed` | BUSINESS | Failed to render template '{template_uri}': {reason} | 模板「{template_uri}」渲染失败:{reason} | | `template.load_failed` | INFRA | Failed to read template resource '{resource_path}' | 模板资源「{resource_path}」读取失败 | | `slot.schema_not_found` | BUSINESS | Template '{template_uri}' is missing its slot schema (language '{language}') | 模板「{template_uri}」缺少参数定义文件(语言「{language}」) | diff --git "a/docs/zh/API\345\217\202\350\200\203.md" "b/docs/zh/API\345\217\202\350\200\203.md" index 6712a7bd..0f298f22 100644 --- "a/docs/zh/API\345\217\202\350\200\203.md" +++ "b/docs/zh/API\345\217\202\350\200\203.md" @@ -1630,7 +1630,7 @@ result.failure() = | 错误码 | 类别 | 消息(zh-CN) | 消息(en-US) | | ----------------------------------------- | -------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| `template.not_found` | BUSINESS | 模板「{template_uri}」不支持语言「{language}」,请检查模板标识与语言配置 | Template '{template_uri}' does not support language '{language}'; check the template URI and language setting | +| `template.not_found` | BUSINESS | 模板「{template_uri}」不存在 | Template '{template_uri}' does not exist | | `template.render_failed` | BUSINESS | 模板「{template_uri}」渲染失败:{reason} | Failed to render template '{template_uri}': {reason} | | `template.load_failed` | INFRA | 模板资源「{resource_path}」读取失败 | Failed to read template resource '{resource_path}' | | `slot.schema_not_found` | BUSINESS | 模板「{template_uri}」缺少参数定义文件(语言「{language}」) | Template '{template_uri}' is missing its slot schema (language '{language}') |