Files
wehub-resource-sync 6ede33ccdb
Build and Push Docker Images / create_manifest (web, surfsense-web, , cpu) (push) Has been cancelled
Build and Push Docker Images / finalize_release (push) Has been cancelled
Obsidian Plugin Lint / lint (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64, , runner, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64, , runner, false, cpu) (push) Has been cancelled
Build and Push Docker Images / compute_version (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, , production, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, , production, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda126, production, true, cuda126) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda126, production, true, cuda126) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda, production, true, cuda) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda, production, true, cuda) (push) Has been cancelled
Build and Push Docker Images / verify_digests (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, , cpu) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda, cuda) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda126, cuda126) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:33:44 +08:00

73 lines
2.4 KiB
Python

"""Shape tool results into the format the caller asked for.
Tools default to Markdown (readable for the model and the human watching), and
can return raw JSON when a caller wants to post-process the data. Large payloads
are clipped so a single call can't blow the context window.
"""
from __future__ import annotations
import json
from typing import Annotated, Any, Literal
from pydantic import Field
ResponseFormat = Literal["markdown", "json"]
# Shared parameter type for every tool: same name, same semantics everywhere.
ResponseFormatParam = Annotated[
ResponseFormat,
Field(
description="'markdown' (default, human-readable) or 'json' "
"(raw data for post-processing)."
),
]
DEFAULT_CLIP_CHARS = 20_000
ITEM_FIELD_CLIP_CHARS = 1_500
# Fields that duplicate another field verbatim (e.g. Reddit's 'html' mirrors
# 'body') and only bloat inline results. The full record stays in the run.
_REDUNDANT_ITEM_FIELDS = frozenset({"html"})
def compact_items(result: Any, field_limit: int = ITEM_FIELD_CLIP_CHARS) -> Any:
"""Shrink a scraper result for inline return.
Drops redundant fields and clips overlong strings per field, so a response
keeps every item as an excerpt instead of a few items in full. The
untruncated result remains retrievable via its stored run.
"""
if isinstance(result, dict) and isinstance(result.get("items"), list):
return {
**result,
"items": [_compact_item(item, field_limit) for item in result["items"]],
}
return result
def _compact_item(item: Any, field_limit: int) -> Any:
# ponytail: compacts top-level string fields only; nested structures pass
# through untouched. Upgrade path is a recursive walk if a platform nests
# long text.
if not isinstance(item, dict):
return item
return {
key: clip(value, field_limit) if isinstance(value, str) else value
for key, value in item.items()
if key not in _REDUNDANT_ITEM_FIELDS
}
def to_json(payload: Any) -> str:
"""Pretty-print a payload as JSON, tolerating non-serializable values."""
return json.dumps(payload, indent=2, ensure_ascii=False, default=str)
def clip(text: str, limit: int = DEFAULT_CLIP_CHARS) -> str:
"""Trim overlong text, leaving a visible marker of how much was dropped."""
if len(text) <= limit:
return text
dropped = len(text) - limit
return f"{text[:limit]}\n\n… [{dropped} more characters truncated]"