Conversation
a221032 to
f699ff3
Compare
f699ff3 to
ea9647a
Compare
ea9647a to
651d3d4
Compare
Classification input goes only into the user message, and a structured answer is checked against the request's categories before it becomes a result. Answers that break the verdict rules are rejected without echoing the input. Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
Request contracts allow empty input, which a plain user message cannot carry. The envelope keeps input as escaped data and is never empty. Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
Classify and assess calls go through the retry template and every outcome maps to a contract result. Unreadable or rule-breaking answers are invalid responses, other failures are unavailability, and interruption is rethrown with the interrupt flag set. Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
Only an interrupted model call escapes as InterruptedException. An interrupt that lands in the wait between attempts now follows the failure table and comes back as unavailable, leaving the thread's interrupt flag set. Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
…supplied model Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
Each entry under embabel.agent.platform.decisions.llm.services becomes a decision or classification service bean named by its key, with its own retry settings. Definitions are typed and eager, so the model provider's LLM lookup skips them and an unknown LLM fails startup naming the property. The platform also exposes an LlmDecisionServiceFactory bean. Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
…eption An interrupt that lands in the wait between attempts now leaves the service the same way as an interrupted model call: as the original InterruptedException with the thread's interrupt flag set. Spring Retry reports the interrupted wait as BackOffInterruptedException with the InterruptedException as its cause, so decide looks for an interruption anywhere in the cause chain before mapping a failure. Observation records these decisions as interrupted instead of failed. Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
The factory constructor takes the internal LlmOperations type, so it is now internal and applications inject the llmDecisionServiceFactory bean. The Java consumer examples are renamed to LlmDecisionServiceJavaTest so Surefire's default includes pick them up in every build. Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
…llers Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
The tag::named[] and tag::supplied[] regions used in the reference docs called the internal LlmDecisionServiceFactory constructor directly. Applications inject the factory bean instead, so both regions now use a factory field built outside the tagged code, matching what real callers would write. Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
…rruption
An interrupted blocking read can surface as ClosedByInterruptException or
another I/O error with no InterruptedException in the chain. The service now
rethrows that as InterruptedException("Decision interrupted") caused by the
failure, both inside the retry guard and in decide, and keeps the flag set.
A SocketTimeoutException with the flag clear stays a retryable failure.
The retry template name is now a constructor parameter, defaulting to
decision-<model>. The decide KDoc now says what this class logs.
Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
The backoff test now uses a sleeper that throws an InterruptedException at once, the way Thread.sleep does when interrupted. It asserts that exact instance comes back. It no longer relies on a thread that interrupts the caller after a delay. A new test covers a flag that is already set before a successful call: the result comes back and the flag stays set. The guard-path test is renamed for the path it exercises, and the decide KDoc says this class logs only the operation, model name and outcome. Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
…try fields The unknown-key check compared an environment variable such as EMBABEL_AGENT_PLATFORM_DECISIONS_LLM_SERVICES_TRIAGE_MAX_ATTEMPTS, which the binder sees as max.attempts, with the bound name max-attempts. It reported the variable as unbound and failed startup on valid configuration. The services map is now bound with UnboundElementsSourceFilter, as Spring Boot does for ignoreUnknownFields = false. Unknown keys in configuration files still fail startup. Unknown keys from environment variables and system properties are not checked. Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
Point the Java examples at LlmDecisionServiceJavaTest. Drop the direct construction guidance and constructor table, since applications inject the factory bean. Describe interruption during the retry wait, the primary ObservationRegistry case, and the retry template's INFO logging of failed attempts. Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
…n and failure rules Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
…ry ID as absent Category descriptions are joined into the classification prompt after trimMargin, so a description line that starts with '|' reaches the model unchanged. NO_MATCH and INCONCLUSIVE now accept an empty or whitespace category ID, the same way SELECTED already treats it as missing. Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
…ilure into an interruption The flag fallback now lives only in the guard around the model call. The decision itself treats only an InterruptedException in the cause chain as an interruption, so a rule-breaking answer is an invalid response whatever the caller's flag says, and the flag is left as the caller set it. Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
…ter the model call Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
…servation and three child chat model observations Builds the service through the factory over ChatClientLlmOperations and a chat model that records its own observation on each call. The first two calls fail with a transient error and the third answers. Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
…rs by hand Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
…prompt Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
|
@jimador - please resolve merge conflicts. |
651d3d4 to
9c00745
Compare
|
from CODEX: Findings
Your other comments make sense as cleanup once the config is moved: using EnableConfigurationProperties, implementing/reusing RetryProperties, naming thanks |
… properties Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
…e platform autoconfiguration Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
…service retry Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
…cellation Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
… autoconfiguration The factory bean and its retry settings move out of embabel-agent-api, so core keeps no Spring Boot wiring for decisions. The factory takes its retry settings as a constructor argument. Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
Signed-off-by: James Dunnam <7660553+jimador@users.noreply.github.com>
|



Decision and classification services answered by a chat model, built on the contracts in #2062. Stacked on #2061, which adds the TypeSafe services for the same contracts.
LlmDecisionServiceFactorybuilds aDecisionServiceor aClassificationServicefrom a model name or from anLlmServicethe caller already holds. The platform autoconfiguration registers the factory as a bean. Services can also be declared in configuration, and the platform autoconfiguration registers each one as aDecisionServicebean named after its key.classDiagram ClassificationService <|-- DecisionService LlmDecisionServiceFactory --> DecisionService : decisionService(llm) LlmDecisionServiceFactory --> ClassificationService : classificationService(llm) LlmDecisionServicesAutoConfiguration --> LlmDecisionServiceFactory : bean LlmDecisionServicesAutoConfiguration --> LlmDecisionServicesRegistrar : registers LlmDecisionServicesRegistrar --> LlmDecisionServiceFactory : builds configured services DecisionService <|.. ObservedDecisionService ClassificationService <|.. ObservedClassificationService ObservedDecisionService --> LlmDecisionService ObservedClassificationService --> LlmClassificationService LlmClassificationService --> LlmDecisionService LlmDecisionService --> LlmOperationsflowchart LR App[Application] --> Observed[ObservedDecisionService] Observed --> Service[LlmDecisionService] Service --> Retry[Retry template] Retry --> Ops[LlmOperations] Ops --> Model[Chat model]Behavior:
LlmServiceis used as is, which covers BYOK: validate the key with the provider's BYOK factory and pass the result.SELECTED,NO_MATCHandINCONCLUSIVEmap toSelected,NoMatchandInconclusive.TRUE,FALSEandINCONCLUSIVEmap toAnswered(true),Answered(false)andInconclusive. Results carry no confidence: the answer schema has no confidence field, and the prompt tells the model not to report one.Failure(INVALID_RESPONSE). A reply that cannot be read is retried and then becomesFailure(INVALID_RESPONSE). A failed model call is retried and then becomesFailure(UNAVAILABLE). The defaults are 5 attempts counting the first, a 100 ms first wait, a multiplier of 5 and at most 180000 ms between attempts.CancellationExceptioncaused by theInterruptedException, and leaves the thread's interrupt flag set. The observation records theinterruptedoutcome.inputfield of a JSON object, so empty input and input that looks like instructions reach it unchanged.Question:line before the categories.embabel.ai.decisionorembabel.ai.classificationobservation. The underlying model call keeps its own observation.@ConfigurationPropertiesunderembabel.agent.platform.decisions.llmand apply to every service the factory builds. The factory takes them as a constructor argument. A setting out of range fails binding, and the cause names the setting.LlmDecisionServicesAutoConfigurationinembabel-agent-platform-autoconfiguresupplies the factory bean, binds the retry settings and registers the configured services. The factory bean backs off when the application defines its own. Core has no Spring Boot wiring for decisions. An entry names itsllm, nothing else. An unknown LLM name or an unknown key under a service in configuration files fails startup, and the error names the property. Keys set through environment variables or system properties are not checked.@ConditionalOnMissingBean. Configured services are built through whichever factory bean is present, so an application's own factory builds them too.No checked exception escapes
classifyorassess, so Java callers never have to handle one. #2080 gives the TypeSafe services andaskthe same convention.The API is experimental. The factory's public surface is its four builder methods. The reference page,
embabel-agent-docs/src/main/asciidoc/reference/llm-decisions/page.adoc, covers configuration, results, failures, interruption and observations.