李刚 / Bryan Li 1fd266e0c0
feat(decisions): add an OpenAI Decisions backend for the decision slots (#7127)
* feat(decisions): add an OpenAI Decisions backend for the decision slots

Issue #7005 asked for a second decision provider behind the interfaces the
TypeSafe (Jev) integration already uses, not a new decision system.
`deerflow/openai_decisions` speaks `POST /v1/decisions`: a `noul` question
becomes a `predicate` whose true/false rubric folds into `instructions` (the
endpoint has no criteria field, and dropping it would move the decision
boundary while the adapter kept reporting the same criteria in its policy
identity), answers arrive as an array rather than a mapping, and a refusal is an
answer `type` rather than an HTTP failure -- so it lands in the existing
per-question error channel and each consumer keeps its own failure direction.

Transport is reused rather than copied. `provider_label` and the connection's
`endpoint_path` are the only two seams a dialect needs, so retry and backoff,
the deadline budget, the bounded never-decoded body read and the error taxonomy
keep one owner; `resolve_decisions_connection` pairs the two so a Decisions
connection cannot be built pointing at System One. The three adapters override
their connection resolution, their client, and the value the endpoint judges
(`input` text instead of a `state` object), and inherit thresholds, rubrics,
caches and audit policy unchanged. Wiring stays a `use:` class path, so no
config schema is added.

* fix(decisions): isolate endpoint identities and correct diagnostics

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 11:50:46 +08:00

46 lines
1.5 KiB
Python

"""OpenAI Decisions transport for the host-side decision consumers.
Sibling of ``deerflow.typesafe`` for issue #7005: the same three consumer slots, the same
policy ownership, a different endpoint dialect.
* ``guardrails/openai_decisions.py`` — the pre-execution tool risk gate.
* ``agents/memory/prescreen/openai_decisions.py`` — the memory capture pre-screen.
* ``agents/memory/signals/openai_decisions.py`` — signal classification.
Shared here: the endpoint, the request envelope, question encoding, answer validation, and the
provider's own defaults. Transport lifecycle, authentication, retry, the deadline budget and
the bounded body read are inherited from ``deerflow.typesafe.client.TypeSafeClient`` rather than
restated, so one owner holds those rules for both backends.
The split of what stays in each adapter is `deerflow.typesafe`'s, unchanged: state content,
questions, criteria, thresholds, failure policy, cache semantics and business audit.
"""
from deerflow.openai_decisions.client import (
ANSWER_TYPE_REFUSAL,
CATEGORY_REFUSAL,
DEFAULT_API_KEY_ENV,
DEFAULT_BASE_URL,
ENDPOINT_PATH,
QUESTION_CHOICE,
QUESTION_PREDICATE,
OpenAIDecisionsClient,
encode_question,
predicate_instructions,
provider_defaults,
)
__all__ = [
"ANSWER_TYPE_REFUSAL",
"CATEGORY_REFUSAL",
"DEFAULT_API_KEY_ENV",
"DEFAULT_BASE_URL",
"ENDPOINT_PATH",
"OpenAIDecisionsClient",
"QUESTION_CHOICE",
"QUESTION_PREDICATE",
"encode_question",
"predicate_instructions",
"provider_defaults",
]