feat(okf): adjudication_for names the unknown state and a tool carries it
Co-Authored-By: Claude <claude-opus-5>
This commit is contained in:
parent
d62e89935c
commit
ccd65d3b01
3 changed files with 191 additions and 1 deletions
57
src/portfolio_optimiser/tools.py
Normal file
57
src/portfolio_optimiser/tools.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
"""Library-level MAF tool carriers over the framework-neutral ``okf`` readers.
|
||||
|
||||
One named carrier per reader whose answer a hypothesiser needs to be able to ASK for. The gate on
|
||||
this module is the carrier, not the vocabulary it carries: an accessor that returns three tokens
|
||||
which nothing propagates is a vocabulary, not a seam.
|
||||
|
||||
**Import direction is one-way and load-bearing:** this module imports ``okf``, never the reverse.
|
||||
``okf`` is MAF-free by invariant (``test_okf_is_maf_free``, D7 vendor-neutrality) and this module
|
||||
is not, so a back-edge would drag ``agent_framework`` into the portable context layer.
|
||||
|
||||
**Not the MCP seam.** ``mcp_tools`` is the opt-in EXTERNAL seam with its own allowlist and
|
||||
announcement rules; a local library primitive placed there would sit behind an egress contract it
|
||||
has no business in. These carriers make no network call and announce nothing.
|
||||
|
||||
**Deliberately NOT wired into ``run_project``.** Wiring a carrier into the run surface would move
|
||||
the byte-pinned demo transcript, which nothing asks for, and would make the state reachable only by
|
||||
reading a run's output rather than by calling a function — the ``evidence_for`` rule, unchanged.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import FunctionTool, tool
|
||||
|
||||
from portfolio_optimiser import okf
|
||||
from portfolio_optimiser.retrieval import safe_resolve
|
||||
|
||||
|
||||
def adjudication_payload(bundle_dir: str, concept: str) -> dict[str, Any]:
|
||||
"""The carrier's answer: the concept asked about, AND the state read for it.
|
||||
|
||||
Both halves are the point. A payload naming the concept without its state would leave
|
||||
``adjudication_for`` perfectly correct and the hypothesiser none the wiser, which is exactly
|
||||
the silent gap this seam closes.
|
||||
|
||||
Path-safe via ``safe_resolve`` (fail-closed), so a concept name escaping the bundle raises
|
||||
rather than reading an arbitrary file."""
|
||||
resolved = safe_resolve(bundle_dir, concept)
|
||||
return {"concept": concept, "adjudication": okf.adjudication_for(resolved)}
|
||||
|
||||
|
||||
def make_adjudication_tool(bundle_dir: str) -> FunctionTool:
|
||||
"""The ONE named carrier exposing a concept's adjudication state to a hypothesiser."""
|
||||
|
||||
@tool(
|
||||
name="concept_adjudication_state",
|
||||
description=(
|
||||
"Report whether a concept in the knowledge base has been adjudicated. Returns "
|
||||
"`proposed`, `adjudicated`, or `unknown` when the concept does not say — `unknown` "
|
||||
"means the state was not learned, never that adjudication did not happen."
|
||||
),
|
||||
)
|
||||
def concept_adjudication_state(concept: str) -> dict[str, Any]:
|
||||
return adjudication_payload(bundle_dir, concept)
|
||||
|
||||
return concept_adjudication_state
|
||||
Loading…
Add table
Add a link
Reference in a new issue