feat: add response cache and compact output mode
Response cache (api-client.ts):
- In-memory TTL cache (5 min default) avoids duplicate API calls
when LLMs repeat the same tool call within a conversation.
- Cache key = full URL with params; only 200 OK responses are cached.
- Exposed via client.cache for manual clear() if needed.
- Configurable TTL via TorrentClawClient constructor.
Compact mode (search_content tool):
- New compact boolean parameter (default: false).
- When true, magnet links use short form magnet:?xt=urn:btih:{hash}
(~60 chars) instead of full magnets with trackers (~300 chars).
- Short magnets are still valid and clickable (DHT peer discovery).
- Also generates a magnet from info_hash even when API returns null.
- Saves ~10K chars per typical search (5 torrents x 10 results).
Tests: 100 total (15 new), all passing.
This commit is contained in:
parent
e011c0f63e
commit
bf459740fe
5 changed files with 349 additions and 9 deletions
|
|
@ -29,7 +29,7 @@ function truncate(text: string, max: number): string {
|
|||
return text.slice(0, max - 3) + "...";
|
||||
}
|
||||
|
||||
function formatTorrent(t: TorrentInfo): string {
|
||||
function formatTorrent(t: TorrentInfo, compact?: boolean): string {
|
||||
const parts: string[] = [];
|
||||
if (t.quality) parts.push(t.quality);
|
||||
if (t.sourceType) parts.push(t.sourceType);
|
||||
|
|
@ -43,11 +43,24 @@ function formatTorrent(t: TorrentInfo): string {
|
|||
let line = ` - ${label} (${size}) | ${seeds}`;
|
||||
if (score) line += ` | ${score}`;
|
||||
line += `\n Info hash: ${t.infoHash}`;
|
||||
if (t.magnetUrl) line += `\n Magnet: ${t.magnetUrl}`;
|
||||
if (compact) {
|
||||
// Short magnet (hash only, no trackers) — still clickable, saves ~200 chars per torrent
|
||||
line += `\n Magnet: magnet:?xt=urn:btih:${t.infoHash}`;
|
||||
} else if (t.magnetUrl) {
|
||||
line += `\n Magnet: ${t.magnetUrl}`;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
function formatResult(r: SearchResult, index: number): string {
|
||||
export interface FormatOptions {
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
function formatResult(
|
||||
r: SearchResult,
|
||||
index: number,
|
||||
opts?: FormatOptions,
|
||||
): string {
|
||||
const lines: string[] = [];
|
||||
const yearStr = r.year ? ` (${r.year})` : "";
|
||||
lines.push(`${index}. ${r.title}${yearStr} [${r.contentType}]`);
|
||||
|
|
@ -65,7 +78,7 @@ function formatResult(r: SearchResult, index: number): string {
|
|||
.slice(0, 5);
|
||||
lines.push(` Torrents (${r.torrents.length} total, top ${top.length}):`);
|
||||
for (const t of top) {
|
||||
lines.push(formatTorrent(t));
|
||||
lines.push(formatTorrent(t, opts?.compact));
|
||||
}
|
||||
} else {
|
||||
lines.push(" No torrents available");
|
||||
|
|
@ -92,13 +105,16 @@ function formatResult(r: SearchResult, index: number): string {
|
|||
return lines.join("\n");
|
||||
}
|
||||
|
||||
export function formatSearchResults(data: SearchResponse): string {
|
||||
export function formatSearchResults(
|
||||
data: SearchResponse,
|
||||
opts?: FormatOptions,
|
||||
): string {
|
||||
if (data.results.length === 0) {
|
||||
return "No results found. Try: (1) a shorter or alternate title, (2) removing filters like quality or year, (3) checking spelling. You can also try get_popular or get_recent to browse available content.";
|
||||
}
|
||||
|
||||
const header = `Found ${data.total} results (page ${data.page}, showing ${data.results.length}):`;
|
||||
const results = data.results.map((r, i) => formatResult(r, i + 1));
|
||||
const results = data.results.map((r, i) => formatResult(r, i + 1, opts));
|
||||
return [header, "", ...results].join("\n");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue