Skip to content

Commit 472dbb8

Browse files
committed
build: regenerate dist after intake extraction
1 parent 7d883f5 commit 472dbb8

3 files changed

Lines changed: 169 additions & 43 deletions

File tree

dist/app.py

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,9 @@ class FrameworkAppConfig(BaseModel):
10051005
escalation_teams: list[str] = Field(default_factory=list)
10061006
severity_aliases: dict[str, str] = Field(default_factory=dict)
10071007
dedup_system_prompt: str = _DEFAULT_DEDUP_SYSTEM_PROMPT
1008+
# Intake runner knobs: forwarded into IntakeContext at graph-build time.
1009+
intake_top_k: int = 3
1010+
intake_similarity_threshold: float = 0.7
10081011

10091012

10101013
def resolve_framework_app_config(
@@ -1644,14 +1647,18 @@ def _validate_supervisor(self) -> None:
16441647
f"skill {self.name!r} (kind=supervisor) requires a non-empty "
16451648
f"subordinates list"
16461649
)
1647-
if self.runner is not None:
1648-
# Resolve at skill-load time so a typo in YAML surfaces here,
1649-
# not in the middle of a session. The resolver itself raises
1650-
# ``ValueError`` with a helpful message — bubble that up.
1651-
_resolve_dotted_callable(
1652-
self.runner,
1653-
source=f"skill {self.name!r} runner",
1654-
)
1650+
if self.runner is None:
1651+
# Default every supervisor to the framework intake runner
1652+
# (similarity retrieval + dedup gate). Apps override by
1653+
# setting ``runner:`` in YAML.
1654+
self.runner = "runtime.intake:default_intake_runner"
1655+
# Resolve at skill-load time so a typo in YAML surfaces here,
1656+
# not in the middle of a session. The resolver itself raises
1657+
# ``ValueError`` with a helpful message — bubble that up.
1658+
_resolve_dotted_callable(
1659+
self.runner,
1660+
source=f"skill {self.name!r} runner",
1661+
)
16551662
if self.dispatch_strategy == "llm" and not self.dispatch_prompt:
16561663
raise ValueError(
16571664
f"skill {self.name!r} (kind=supervisor, strategy=llm) requires "
@@ -4144,8 +4151,13 @@ def _sqlite_path_from_url(url: str) -> str:
41444151
if path.startswith("//"):
41454152
# sqlite:////abs/path -> urlparse path "//abs/path" -> "/abs/path"
41464153
return path[1:]
4147-
# sqlite:///x -> urlparse path "/x". sqlite3 accepts both absolute
4148-
# and relative; tests use absolute via tmp_path.
4154+
# sqlite:///<rel> -> urlparse path "/<rel>". SQLAlchemy treats this
4155+
# as a path *relative* to CWD; strip the leading slash so the helper
4156+
# agrees (otherwise mkdir tries the filesystem root). Tests pass
4157+
# absolute paths via tmp_path which composes to the four-slash form
4158+
# above, so this branch only matters for the relative case.
4159+
if path.startswith("/"):
4160+
return path[1:]
41494161
return path
41504162

41514163

@@ -5570,6 +5582,7 @@ async def _stage2(
55705582

55715583

55725584

5585+
55735586
from langgraph.types import Command
55745587

55755588

@@ -5841,6 +5854,20 @@ async def create(cls, cfg: AppConfig) -> "Orchestrator":
58415854
similarity_threshold=framework_cfg.similarity_threshold,
58425855
distance_strategy=cfg.storage.vector.distance_strategy,
58435856
)
5857+
# Attach intake_context onto framework_cfg so supervisor nodes can
5858+
# reach the live stores via app_cfg.intake_context. FrameworkAppConfig
5859+
# is a Pydantic model; use object.__setattr__ to set a runtime
5860+
# attribute without triggering Pydantic's frozen-model guard.
5861+
object.__setattr__(
5862+
framework_cfg,
5863+
"intake_context",
5864+
IntakeContext(
5865+
history_store=history,
5866+
dedup_pipeline=None, # dedup_pipeline built below; patched after
5867+
top_k=framework_cfg.intake_top_k,
5868+
similarity_threshold=framework_cfg.intake_similarity_threshold,
5869+
),
5870+
)
58445871
# Configure incident_management state via importlib so we hit the
58455872
# *same* module instance the MCP loader will import. In the
58465873
# single-file dist bundle a direct ``set_state`` call would
@@ -5918,6 +5945,11 @@ def _factory():
59185945
model_factory=_factory,
59195946
framework_cfg=framework_cfg,
59205947
)
5948+
# Backfill dedup_pipeline into the IntakeContext now that it is built.
5949+
# The IntakeContext was constructed with dedup_pipeline=None above
5950+
# because the pipeline is built after graph construction.
5951+
if dedup_pipeline is not None:
5952+
framework_cfg.intake_context.dedup_pipeline = dedup_pipeline
59215953
# P2-I: no resume graph anymore — resume runs through the
59225954
# main graph via ``Command(resume=...)`` against the same
59235955
# thread_id, with the checkpointer rehydrates paused state.

dist/apps/code-review.py

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,9 @@ class FrameworkAppConfig(BaseModel):
10521052
escalation_teams: list[str] = Field(default_factory=list)
10531053
severity_aliases: dict[str, str] = Field(default_factory=dict)
10541054
dedup_system_prompt: str = _DEFAULT_DEDUP_SYSTEM_PROMPT
1055+
# Intake runner knobs: forwarded into IntakeContext at graph-build time.
1056+
intake_top_k: int = 3
1057+
intake_similarity_threshold: float = 0.7
10551058

10561059

10571060
def resolve_framework_app_config(
@@ -1691,14 +1694,18 @@ def _validate_supervisor(self) -> None:
16911694
f"skill {self.name!r} (kind=supervisor) requires a non-empty "
16921695
f"subordinates list"
16931696
)
1694-
if self.runner is not None:
1695-
# Resolve at skill-load time so a typo in YAML surfaces here,
1696-
# not in the middle of a session. The resolver itself raises
1697-
# ``ValueError`` with a helpful message — bubble that up.
1698-
_resolve_dotted_callable(
1699-
self.runner,
1700-
source=f"skill {self.name!r} runner",
1701-
)
1697+
if self.runner is None:
1698+
# Default every supervisor to the framework intake runner
1699+
# (similarity retrieval + dedup gate). Apps override by
1700+
# setting ``runner:`` in YAML.
1701+
self.runner = "runtime.intake:default_intake_runner"
1702+
# Resolve at skill-load time so a typo in YAML surfaces here,
1703+
# not in the middle of a session. The resolver itself raises
1704+
# ``ValueError`` with a helpful message — bubble that up.
1705+
_resolve_dotted_callable(
1706+
self.runner,
1707+
source=f"skill {self.name!r} runner",
1708+
)
17021709
if self.dispatch_strategy == "llm" and not self.dispatch_prompt:
17031710
raise ValueError(
17041711
f"skill {self.name!r} (kind=supervisor, strategy=llm) requires "
@@ -4191,8 +4198,13 @@ def _sqlite_path_from_url(url: str) -> str:
41914198
if path.startswith("//"):
41924199
# sqlite:////abs/path -> urlparse path "//abs/path" -> "/abs/path"
41934200
return path[1:]
4194-
# sqlite:///x -> urlparse path "/x". sqlite3 accepts both absolute
4195-
# and relative; tests use absolute via tmp_path.
4201+
# sqlite:///<rel> -> urlparse path "/<rel>". SQLAlchemy treats this
4202+
# as a path *relative* to CWD; strip the leading slash so the helper
4203+
# agrees (otherwise mkdir tries the filesystem root). Tests pass
4204+
# absolute paths via tmp_path which composes to the four-slash form
4205+
# above, so this branch only matters for the relative case.
4206+
if path.startswith("/"):
4207+
return path[1:]
41964208
return path
41974209

41984210

@@ -5617,6 +5629,7 @@ async def _stage2(
56175629

56185630

56195631

5632+
56205633
from langgraph.types import Command
56215634

56225635

@@ -5888,6 +5901,20 @@ async def create(cls, cfg: AppConfig) -> "Orchestrator":
58885901
similarity_threshold=framework_cfg.similarity_threshold,
58895902
distance_strategy=cfg.storage.vector.distance_strategy,
58905903
)
5904+
# Attach intake_context onto framework_cfg so supervisor nodes can
5905+
# reach the live stores via app_cfg.intake_context. FrameworkAppConfig
5906+
# is a Pydantic model; use object.__setattr__ to set a runtime
5907+
# attribute without triggering Pydantic's frozen-model guard.
5908+
object.__setattr__(
5909+
framework_cfg,
5910+
"intake_context",
5911+
IntakeContext(
5912+
history_store=history,
5913+
dedup_pipeline=None, # dedup_pipeline built below; patched after
5914+
top_k=framework_cfg.intake_top_k,
5915+
similarity_threshold=framework_cfg.intake_similarity_threshold,
5916+
),
5917+
)
58915918
# Configure incident_management state via importlib so we hit the
58925919
# *same* module instance the MCP loader will import. In the
58935920
# single-file dist bundle a direct ``set_state`` call would
@@ -5965,6 +5992,11 @@ def _factory():
59655992
model_factory=_factory,
59665993
framework_cfg=framework_cfg,
59675994
)
5995+
# Backfill dedup_pipeline into the IntakeContext now that it is built.
5996+
# The IntakeContext was constructed with dedup_pipeline=None above
5997+
# because the pipeline is built after graph construction.
5998+
if dedup_pipeline is not None:
5999+
framework_cfg.intake_context.dedup_pipeline = dedup_pipeline
59686000
# P2-I: no resume graph anymore — resume runs through the
59696001
# main graph via ``Command(resume=...)`` against the same
59706002
# thread_id, with the checkpointer rehydrates paused state.

dist/apps/incident-management.py

Lines changed: 85 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,6 +1289,9 @@ class FrameworkAppConfig(BaseModel):
12891289
escalation_teams: list[str] = Field(default_factory=list)
12901290
severity_aliases: dict[str, str] = Field(default_factory=dict)
12911291
dedup_system_prompt: str = _DEFAULT_DEDUP_SYSTEM_PROMPT
1292+
# Intake runner knobs: forwarded into IntakeContext at graph-build time.
1293+
intake_top_k: int = 3
1294+
intake_similarity_threshold: float = 0.7
12921295

12931296

12941297
def resolve_framework_app_config(
@@ -1928,14 +1931,18 @@ def _validate_supervisor(self) -> None:
19281931
f"skill {self.name!r} (kind=supervisor) requires a non-empty "
19291932
f"subordinates list"
19301933
)
1931-
if self.runner is not None:
1932-
# Resolve at skill-load time so a typo in YAML surfaces here,
1933-
# not in the middle of a session. The resolver itself raises
1934-
# ``ValueError`` with a helpful message — bubble that up.
1935-
_resolve_dotted_callable(
1936-
self.runner,
1937-
source=f"skill {self.name!r} runner",
1938-
)
1934+
if self.runner is None:
1935+
# Default every supervisor to the framework intake runner
1936+
# (similarity retrieval + dedup gate). Apps override by
1937+
# setting ``runner:`` in YAML.
1938+
self.runner = "runtime.intake:default_intake_runner"
1939+
# Resolve at skill-load time so a typo in YAML surfaces here,
1940+
# not in the middle of a session. The resolver itself raises
1941+
# ``ValueError`` with a helpful message — bubble that up.
1942+
_resolve_dotted_callable(
1943+
self.runner,
1944+
source=f"skill {self.name!r} runner",
1945+
)
19391946
if self.dispatch_strategy == "llm" and not self.dispatch_prompt:
19401947
raise ValueError(
19411948
f"skill {self.name!r} (kind=supervisor, strategy=llm) requires "
@@ -4428,8 +4435,13 @@ def _sqlite_path_from_url(url: str) -> str:
44284435
if path.startswith("//"):
44294436
# sqlite:////abs/path -> urlparse path "//abs/path" -> "/abs/path"
44304437
return path[1:]
4431-
# sqlite:///x -> urlparse path "/x". sqlite3 accepts both absolute
4432-
# and relative; tests use absolute via tmp_path.
4438+
# sqlite:///<rel> -> urlparse path "/<rel>". SQLAlchemy treats this
4439+
# as a path *relative* to CWD; strip the leading slash so the helper
4440+
# agrees (otherwise mkdir tries the filesystem root). Tests pass
4441+
# absolute paths via tmp_path which composes to the four-slash form
4442+
# above, so this branch only matters for the relative case.
4443+
if path.startswith("/"):
4444+
return path[1:]
44334445
return path
44344446

44354447

@@ -5854,6 +5866,7 @@ async def _stage2(
58545866

58555867

58565868

5869+
58575870
from langgraph.types import Command
58585871

58595872

@@ -6125,6 +6138,20 @@ async def create(cls, cfg: AppConfig) -> "Orchestrator":
61256138
similarity_threshold=framework_cfg.similarity_threshold,
61266139
distance_strategy=cfg.storage.vector.distance_strategy,
61276140
)
6141+
# Attach intake_context onto framework_cfg so supervisor nodes can
6142+
# reach the live stores via app_cfg.intake_context. FrameworkAppConfig
6143+
# is a Pydantic model; use object.__setattr__ to set a runtime
6144+
# attribute without triggering Pydantic's frozen-model guard.
6145+
object.__setattr__(
6146+
framework_cfg,
6147+
"intake_context",
6148+
IntakeContext(
6149+
history_store=history,
6150+
dedup_pipeline=None, # dedup_pipeline built below; patched after
6151+
top_k=framework_cfg.intake_top_k,
6152+
similarity_threshold=framework_cfg.intake_similarity_threshold,
6153+
),
6154+
)
61286155
# Configure incident_management state via importlib so we hit the
61296156
# *same* module instance the MCP loader will import. In the
61306157
# single-file dist bundle a direct ``set_state`` call would
@@ -6202,6 +6229,11 @@ def _factory():
62026229
model_factory=_factory,
62036230
framework_cfg=framework_cfg,
62046231
)
6232+
# Backfill dedup_pipeline into the IntakeContext now that it is built.
6233+
# The IntakeContext was constructed with dedup_pipeline=None above
6234+
# because the pipeline is built after graph construction.
6235+
if dedup_pipeline is not None:
6236+
framework_cfg.intake_context.dedup_pipeline = dedup_pipeline
62056237
# P2-I: no resume graph anymore — resume runs through the
62066238
# main graph via ``Command(resume=...)`` against the same
62076239
# thread_id, with the checkpointer rehydrates paused state.
@@ -8026,7 +8058,7 @@ def hydrate_and_gate(
80268058
# a ``runner`` extension point: a callable invoked at supervisor-node entry
80278059
# that may mutate ``GraphState`` and/or short-circuit to ``"__end__"``. The
80288060
# functions below adapt :func:`hydrate_and_gate` to that contract so the
8029-
# asr_supervisor skill actually exercises the hydration + dup-gate logic
8061+
# intake skill actually exercises the hydration + dup-gate logic
80308062
# inside the live graph, instead of leaving it to be called only from
80318063
# unit tests.
80328064
#
@@ -8114,31 +8146,61 @@ def _runner(state: Any, *, app_cfg: Any | None = None) -> dict[str, Any] | None:
81148146
return _runner
81158147

81168148

8117-
def default_supervisor_runner(
8118-
state: Any, *, app_cfg: Any | None = None,
8119-
) -> dict[str, Any] | None:
8120-
"""Module-level runner the YAML can wire in via dotted path.
81218149

8122-
Anchored on the bundled seed directory (``seeds/kg``,
8123-
``seeds/releases``, ``seeds/playbooks``) so the asr_supervisor
8124-
skill works out of the box. Real deployments should construct
8125-
their own runner via :func:`make_hydrate_runner` and register it
8126-
with their app's skill loader.
8150+
def make_default_supervisor_runner(
8151+
*,
8152+
kg_store: KGStore,
8153+
release_store: ReleaseStore,
8154+
playbook_store: PlaybookStore,
8155+
get_active_sessions: Callable[[], list[dict[str, Any]]] | None = None,
8156+
component_lookup: Callable[[str], list[str]] | None = None,
8157+
) -> Callable[..., dict[str, Any] | None]:
8158+
"""Compose framework default_intake_runner + ASR memory hydration.
8159+
8160+
Framework default runs first (similarity retrieval + dedup gate).
8161+
If it short-circuits via ``next_route='__end__'`` (duplicate), the
8162+
ASR hydration is skipped — the duplicate session ends without
8163+
paying for KG/playbook lookups.
81278164
"""
8128-
runner = _BUILT_DEFAULT_RUNNER
8129-
return runner(state, app_cfg=app_cfg)
8165+
asr_runner = make_hydrate_runner(
8166+
kg_store=kg_store,
8167+
release_store=release_store,
8168+
playbook_store=playbook_store,
8169+
get_active_sessions=get_active_sessions,
8170+
component_lookup=component_lookup,
8171+
)
8172+
return compose_runners(default_intake_runner, asr_runner)
81308173

81318174

81328175
# Build the default runner exactly once at import time so per-call
81338176
# overhead is just a closure invocation. Constructor stays cheap:
81348177
# the stores read seed JSON lazily on first access.
8135-
_BUILT_DEFAULT_RUNNER = make_hydrate_runner(
8178+
_BUILT_DEFAULT_RUNNER = make_default_supervisor_runner(
81368179
kg_store=KGStore(_DEFAULT_SEEDS / "kg"),
81378180
release_store=ReleaseStore(_DEFAULT_SEEDS / "releases"),
81388181
playbook_store=PlaybookStore(_DEFAULT_SEEDS / "playbooks"),
81398182
get_active_sessions=lambda: [],
81408183
)
81418184

8185+
8186+
def default_supervisor_runner(
8187+
state: Any, *, app_cfg: Any | None = None,
8188+
) -> dict[str, Any] | None:
8189+
"""Module-level runner the YAML can wire in via dotted path.
8190+
8191+
Anchored on the bundled seed directory (``seeds/kg``,
8192+
``seeds/releases``, ``seeds/playbooks``) so the intake
8193+
skill works out of the box. Real deployments should construct
8194+
their own runner via :func:`make_default_supervisor_runner` and
8195+
register it with their app's skill loader.
8196+
8197+
Composition: framework ``default_intake_runner`` (similarity
8198+
retrieval + dedup gate) runs first; ASR memory hydration follows.
8199+
If the framework short-circuits (``next_route='__end__'``), the
8200+
hydration step is skipped.
8201+
"""
8202+
return _BUILT_DEFAULT_RUNNER(state, app_cfg=app_cfg)
8203+
81428204
# ====== module: examples/incident_management/asr/hypothesis_loop.py ======
81438205

81448206
MAX_ITERATIONS: int = 3

0 commit comments

Comments
 (0)