JITContextManager -- minimal prompt, no inline hydration.
Maps to the pre-7.2 FrameworkConfig.jit_context=True path. The
strategy renders the ~210-token JIT system prompt and returns
memory_context=None so the engine skips the
invocation_state["memory_context"] stamping. Memory is loaded
reactively by the LLM via the hydrate_context tool, not by this
strategy.
Type-level invariant. :class:JITContextManager does NOT accept a
:class:MemoryOrchestrator argument. The orchestrator is unreachable
from the JIT path by construction; the "no hydration" guarantee is
visible in the constructor signature.
JITContextManager
JITContextManager(
*, build_prompt: Callable[..., Awaitable[str | None]]
)
Reactive-hydration strategy. Returns memory_context=None.
Constructor takes a single callable -- build_prompt -- that
renders the JIT system prompt. The engine wires this to its
existing _build_jit_system_prompt method so the byte-for-byte
template-rendering path is preserved. The strategy itself is a
thin orchestration shell.
Wire the strategy to the engine's JIT prompt builder.
Parameters:
| Name |
Type |
Description |
Default |
build_prompt
|
Callable[..., Awaitable[str | None]]
|
Async callable with signature
(tenant_id: str, namespace: str, *, query: str,
scope: FrameworkTenantScope | None = None) -> str | None.
The engine binds this to
:meth:SymfonicAgent._build_jit_prompt_for_strategy.
v8.0: scope is forwarded so the synth-time L1
PRE-FLIGHT state carries the full multi-level path, not
just the tenant_id / namespace scalars (the
strategy boundary previously destroyed the brand/org
levels).
|
required
|
Source code in src/symfonic/agent/context/jit.py
| def __init__(
self,
*,
build_prompt: Callable[..., Awaitable[str | None]],
) -> None:
"""Wire the strategy to the engine's JIT prompt builder.
Args:
build_prompt: Async callable with signature
``(tenant_id: str, namespace: str, *, query: str,
scope: FrameworkTenantScope | None = None) -> str | None``.
The engine binds this to
:meth:`SymfonicAgent._build_jit_prompt_for_strategy`.
v8.0: ``scope`` is forwarded so the synth-time L1
PRE-FLIGHT state carries the full multi-level path, not
just the ``tenant_id`` / ``namespace`` scalars (the
strategy boundary previously destroyed the brand/org
levels).
"""
self._build_prompt = build_prompt
|
assemble
async
assemble(
*,
query: str,
scope: FrameworkTenantScope | None,
intent_verdict: Any | None,
auto_hydrate: bool = True,
) -> AssembledContext
Render the JIT prompt; skip hydration unconditionally.
intent_verdict is accepted to satisfy the
:class:ContextManager protocol but is ignored in v1 (OQ-1 in
the design note resolves to "defer reactive in-strategy
hydration to 7.3"). Reactive hydration still works -- the LLM
calls the hydrate_context tool mid-turn via the tool-call
channel.
v7.7.8: auto_hydrate accepted to satisfy the updated
protocol; JIT never hydrates inline so the flag is informational
only. Critical for parity with stratified: scope reaches the
prompt template regardless of the flag.
Source code in src/symfonic/agent/context/jit.py
| async def assemble(
self,
*,
query: str,
scope: FrameworkTenantScope | None,
intent_verdict: Any | None,
auto_hydrate: bool = True,
) -> AssembledContext:
"""Render the JIT prompt; skip hydration unconditionally.
``intent_verdict`` is accepted to satisfy the
:class:`ContextManager` protocol but is ignored in v1 (OQ-1 in
the design note resolves to "defer reactive in-strategy
hydration to 7.3"). Reactive hydration still works -- the LLM
calls the ``hydrate_context`` tool mid-turn via the tool-call
channel.
v7.7.8: ``auto_hydrate`` accepted to satisfy the updated
protocol; JIT never hydrates inline so the flag is informational
only. Critical for parity with stratified: scope reaches the
prompt template regardless of the flag.
"""
_ = intent_verdict # OQ-1 resolution: ignored in v1.
_ = auto_hydrate # JIT never hydrates inline; flag noted only.
start = time.monotonic()
if scope is not None:
tenant_id = scope.tenant_id
namespace = scope.namespace or scope.sub_tenant_id or "default"
else:
tenant_id = "unknown"
namespace = "default"
system_prompt = await self._build_prompt(
tenant_id=tenant_id,
namespace=namespace,
query=query,
# v8.0 BLOCKER FIX: thread the FULL scope object through so the
# L1 PRE-FLIGHT synth-state carries the multi-level path, not
# just tenant_id/namespace scalars. Without this the JIT
# strategy boundary destroyed the brand/org levels and the
# active-skills getter reconstructed a 1-level scope, hiding
# brand-level PRE-FLIGHT skills under always-on prefix isolation.
scope=scope,
)
latency_ms = (time.monotonic() - start) * 1000.0
return AssembledContext(
system_prompt=system_prompt,
memory_context=None,
state_overrides={},
activation_log=None,
metadata={
"strategy_name": "jit",
"latency_ms": latency_ms,
"entries_count": 0,
},
)
|