Skip to content

Latest commit

 

History

History
272 lines (196 loc) · 8.77 KB

File metadata and controls

272 lines (196 loc) · 8.77 KB
title Offload Response Structure
diataxis_type reference

Offload Response Structure

This document specifies the exact dict structure returned by LROOffloader.offload_if_needed (and by extension, lro_offload and LROContext) when offloading activates.


Decision Outcomes

offload_if_needed returns one of three possible outcomes:

Condition Return value
enabled is False Original data dict, unchanged.
estimate_size(data) <= threshold Original data dict, unchanged.
Threshold exceeded, write succeeds Offload descriptor (documented below).
Threshold exceeded, write fails Fallback response (documented below).

Offload Descriptor

When offloading activates and all JSONL writes succeed, the returned dict has the following structure:

Top-Level Fields

Field Type Description
offloaded bool Always True.
(inline keys) (varies) Zero or more keys copied verbatim from the original data dict, as specified by the inline_keys parameter.
summary dict[str, dict[str, Any]] Per-section summary produced by each section's summarizer.
files dict[str, str] Per-section mapping of section key to absolute file path string.
schemas dict[str, dict[str, Any]] Per-section schema information.
jq_recipes dict[str, str] Merged dict of all sections' jq extraction recipes.
guidance str Auto-generated advisory text for the consuming LLM.

Field Ordering

Fields appear in this order in the returned dict:

  1. offloaded
  2. Inline keys (in the order specified by inline_keys, if present in data)
  3. summary
  4. files
  5. schemas
  6. jq_recipes
  7. guidance

Detailed Field Descriptions

offloaded

"offloaded": True

Always True. Use this field to detect whether a response has been offloaded.

Inline Keys

Keys listed in the inline_keys parameter are copied from the original data dict into the offload descriptor. Only keys that exist in data are included; missing keys are silently skipped.

# Given inline_keys=["query", "total_count"] and data={"query": "test", "total_count": 500, ...}
"query": "test",
"total_count": 500,

summary

"summary": {
    "results": {"count": 247},
    "metadata": {"count": 15},
}

A dict mapping each section key to the output of that section's summarizer function.

Default summarizer output:

Items type Output
list {"count": len(items)}
dict {"count": len(items)}
Other {}

Custom summarizers (set via OffloadSection.summarize) can return any dict[str, Any].

Sections whose key is not present in the original data are omitted from the summary.

files

"files": {
    "results": "/tmp/mcp-lro/search-sess_001-20260325T225815Z-a1b2c3d4.jsonl",
    "metadata": "/tmp/mcp-lro/meta-sess_001-20260325T225815Z-a1b2c3d4.jsonl",
}

A dict mapping each section key to the absolute file path (as a str) of the written JSONL file.

  • Paths are resolved (Path.resolve()) before inclusion.
  • Sections whose key is not in data are omitted.
  • Sections that fail the path traversal security check are omitted.

schemas

"schemas": {
    "results": {
        "description": "Line 1 is header metadata. Lines 2+ are data objects.",
        "line_schema": {"id": "string", "title": "string", "score": "number"},
    },
}

A dict mapping each section key to a schema descriptor with two fields:

Sub-field Type Source
description str OffloadSection.schema_description
line_schema dict[str, Any] OffloadSection.schema

jq_recipes

"jq_recipes": {
    "list_all_results": "tail -n +2 '{results_file}' | jq '.'",
    "count_results": "tail -n +2 '{results_file}' | jq -s 'length'",
    "first_10_results": "tail -n +2 '{results_file}' | head -10 | jq '.'",
}

A merged dict of all sections' jq recipes. The file path placeholder in each recipe is {section_key_file} (e.g., {results_file}).

Recipe sources:

  • If OffloadSection.jq_recipes is provided, those recipes are used as-is (no placeholder substitution is performed by the library on custom recipes).
  • If OffloadSection.jq_recipes is None, three auto-generated recipes are produced per section. See OffloadSection.get_jq_recipes.

When multiple sections produce recipes, all are merged into a single dict. If two sections produce recipes with the same name, the later section's recipe overwrites the earlier one.

guidance

"guidance": (
    "Results offloaded to JSONL (2 section(s): results, metadata).\n"
    "Files:\n"
    "  - results: /tmp/mcp-lro/search-sess_001-20260325T225815Z-a1b2c3d4.jsonl\n"
    "  - metadata: /tmp/mcp-lro/meta-sess_001-20260325T225815Z-a1b2c3d4.jsonl\n"
    "\n"
    "Use the jq_recipes above to extract specific data, or read files directly.\n"
    "Header line (line 1) contains metadata; data objects start at line 2."
)

Auto-generated advisory text. Format:

Results offloaded to JSONL ({N} section(s): {comma-separated section keys}).
Files:
  - {key1}: {path1}
  - {key2}: {path2}

Use the jq_recipes above to extract specific data, or read files directly.
Header line (line 1) contains metadata; data objects start at line 2.

The guidance is always present when offloading succeeds. It is a plain string, not Markdown.


Complete Offload Descriptor Example

Given:

  • data = {"query": "test", "total_count": 500, "results": [... 247 items ...]}
  • inline_keys = ["query", "total_count"]
  • sections with one section: key "results", prefix "search", default summarizer
{
    "offloaded": True,
    "query": "test",
    "total_count": 500,
    "summary": {
        "results": {"count": 247},
    },
    "files": {
        "results": "/tmp/mcp-lro/search-sess_001-20260325T225815Z-a1b2c3d4.jsonl",
    },
    "schemas": {
        "results": {
            "description": "Line 1 is header metadata. Lines 2+ are data objects.",
            "line_schema": {"id": "string", "title": "string", "score": "number"},
        },
    },
    "jq_recipes": {
        "list_all_results": "tail -n +2 '{results_file}' | jq '.'",
        "count_results": "tail -n +2 '{results_file}' | jq -s 'length'",
        "first_10_results": "tail -n +2 '{results_file}' | head -10 | jq '.'",
    },
    "guidance": "Results offloaded to JSONL (1 section(s): results).\nFiles:\n  - results: /tmp/mcp-lro/search-sess_001-20260325T225815Z-a1b2c3d4.jsonl\n\nUse the jq_recipes above to extract specific data, or read files directly.\nHeader line (line 1) contains metadata; data objects start at line 2.",
}

Fallback Response (Write Failure)

When the JSONL write fails due to an OSError or TypeError, the method:

  1. Logs the error.
  2. Attempts to delete the partial file (suppressing any OSError during deletion).
  3. Returns the original data dict with an appended warning field.

Structure

{**data, "lro_warning": "Offload failed: {exception_message}"}

Fields:

Field Type Description
(all original data keys) (varies) The complete original data dict, spread into the return value.
lro_warning str Error message in the format "Offload failed: {str(exc)}".

Example

{
    "query": "test",
    "total_count": 500,
    "results": [... original items ...],
    "lro_warning": "Offload failed: [Errno 28] No space left on device: '/tmp/mcp-lro/search-sess_001-20260325T225815Z-a1b2c3d4.jsonl'",
}

Failure Conditions

The fallback triggers on:

  • OSError during file write (disk full, permission denied, etc.)
  • TypeError during JSON serialization (non-serializable objects in data lines)

The fallback does not trigger on:

  • Output directory errors (symlink check, creation failure) -- these are caught earlier and return data unchanged.
  • Path traversal failures for individual sections -- those sections are skipped; other sections may still succeed.

Edge Cases

No Sections Match

If none of the declared sections' keys exist in data, the offload descriptor is still returned with empty files, schemas, summary, and jq_recipes dicts, and offloaded: True.

Multiple Sections

Each section produces its own JSONL file, summary entry, schema entry, and jq recipes. All are merged into the single offload descriptor. The timestamp and uuid8 components are shared across all sections within a single offload_if_needed call.

Non-Dict Return (Decorator Only)

When using the lro_offload decorator, if the wrapped function returns a non-dict value, it is returned unchanged. No offload evaluation occurs.