Skip to content

symfonic.agent.prompts.jit_manifest

jit_manifest

JIT compressed tool-manifest helper.

v6.2 T04: optional compressed tool-schema injection for the JIT system prompt. Lifted into its own module to keep engine.py line count in check (the ~100-line compressor landing on top of an already 3300-line module would push it further past the 300-LOC-per-file cap the repo policy calls out).

The function is called from :meth:SymfonicAgent._build_jit_system_prompt only when FrameworkConfig.jit_manifest_token_budget > 0; the zero branch still shortcircuits at the caller so the cost is literally zero for JIT deployments that do not opt in.

compress_jit_manifest

compress_jit_manifest(
    manifest: list[str],
    trigger_keywords: dict[str, list[str]],
    query: str,
    token_budget: int,
    *,
    filter_fn: Callable[
        [list[str], dict[str, list[str]], str], list[str]
    ],
) -> str

Render a keyword-filtered, budget-capped tool manifest for JIT mode.

Output shape: one line per surviving tool, two-space-indented, e.g.::

- calendar: book a meeting
- email: send outbound email
- ... (+3 more)

The final "(+N more)" footer is only appended when the budget forced entries to be dropped; at that point N is the number of filtered entries past the budget cap. When no entries fit we return the literal (omitted — budget too small) instead of an empty block so the rendered prompt is still self-describing.

Delegates keyword gating to the injected filter_fn (normally :func:symfonic.agent.engine._filter_manifest_by_keywords) so the JIT path stays consistent with the non-JIT HMS prompt's trigger-keyword behaviour. The callback is injected rather than imported to avoid a circular import -- engine imports this module, not the other way around.

Source code in src/symfonic/agent/prompts/jit_manifest.py
def compress_jit_manifest(
    manifest: list[str],
    trigger_keywords: dict[str, list[str]],
    query: str,
    token_budget: int,
    *,
    filter_fn: Callable[[list[str], dict[str, list[str]], str], list[str]],
) -> str:
    """Render a keyword-filtered, budget-capped tool manifest for JIT mode.

    Output shape: one line per surviving tool, two-space-indented, e.g.::

        - calendar: book a meeting
        - email: send outbound email
        - ... (+3 more)

    The final "(+N more)" footer is only appended when the budget forced
    entries to be dropped; at that point N is the number of filtered
    entries past the budget cap. When no entries fit we return the
    literal ``(omitted — budget too small)`` instead of an empty block
    so the rendered prompt is still self-describing.

    Delegates keyword gating to the injected ``filter_fn`` (normally
    :func:`symfonic.agent.engine._filter_manifest_by_keywords`) so the
    JIT path stays consistent with the non-JIT HMS prompt's
    trigger-keyword behaviour. The callback is injected rather than
    imported to avoid a circular import -- ``engine`` imports this
    module, not the other way around.
    """
    if not manifest:
        return "  (no tools registered)"
    filtered = filter_fn(manifest, trigger_keywords, query)
    if not filtered:
        # Keyword gating dropped every tool for this query. Surface a
        # deterministic marker instead of an empty string.
        return "  (no tools match this query)"

    # Budget in chars. A single "  - x\n" line costs ~6 chars; we give
    # up trying to be clever past the budget -- straight line-by-line
    # accumulation until the cap, with a trailing " (+N more)" note.
    char_cap = max(0, token_budget) * JIT_CHARS_PER_TOKEN
    lines: list[str] = []
    dropped = 0
    running = 0
    footer_reserve = len("  ... (+000 more)")
    for entry in filtered:
        line = f"  - {entry}"
        # Reserve space for the footer line on every step so the final
        # rendering is never over-budget. Budget is checked BEFORE the
        # first append so a budget of 0 (or too small for any entry)
        # yields an empty ``lines`` list and the deterministic marker.
        if running + len(line) + 1 + footer_reserve > char_cap:
            dropped = len(filtered) - len(lines)
            break
        lines.append(line)
        running += len(line) + 1  # +1 for the newline.

    if not lines:
        return "  (omitted — budget too small)"
    body = "\n".join(lines)
    if dropped > 0:
        body += f"\n  ... (+{dropped} more)"
    return body