@cloudwarriors-ai/rlm 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/application/handlers/llm-query-handler.d.ts +67 -0
  2. package/dist/application/handlers/llm-query-handler.d.ts.map +1 -0
  3. package/dist/application/handlers/llm-query-handler.js +169 -0
  4. package/dist/application/handlers/llm-query-handler.js.map +1 -0
  5. package/dist/application/query-handler.d.ts +23 -2
  6. package/dist/application/query-handler.d.ts.map +1 -1
  7. package/dist/application/query-handler.js +215 -112
  8. package/dist/application/query-handler.js.map +1 -1
  9. package/dist/cli/index.js +0 -0
  10. package/dist/domain/constants.d.ts +124 -0
  11. package/dist/domain/constants.d.ts.map +1 -0
  12. package/dist/domain/constants.js +148 -0
  13. package/dist/domain/constants.js.map +1 -0
  14. package/dist/domain/errors/index.d.ts +1 -0
  15. package/dist/domain/errors/index.d.ts.map +1 -1
  16. package/dist/domain/errors/index.js +2 -0
  17. package/dist/domain/errors/index.js.map +1 -1
  18. package/dist/domain/errors/token-budget-error.d.ts +47 -0
  19. package/dist/domain/errors/token-budget-error.d.ts.map +1 -0
  20. package/dist/domain/errors/token-budget-error.js +41 -0
  21. package/dist/domain/errors/token-budget-error.js.map +1 -0
  22. package/dist/domain/interfaces/code-executor.d.ts +32 -2
  23. package/dist/domain/interfaces/code-executor.d.ts.map +1 -1
  24. package/dist/domain/interfaces/event-emitter.d.ts +55 -1
  25. package/dist/domain/interfaces/event-emitter.d.ts.map +1 -1
  26. package/dist/domain/interfaces/llm-provider.d.ts +4 -0
  27. package/dist/domain/interfaces/llm-provider.d.ts.map +1 -1
  28. package/dist/domain/services/cost-calculator.d.ts.map +1 -1
  29. package/dist/domain/services/cost-calculator.js +9 -4
  30. package/dist/domain/services/cost-calculator.js.map +1 -1
  31. package/dist/domain/types/config.d.ts +17 -0
  32. package/dist/domain/types/config.d.ts.map +1 -1
  33. package/dist/domain/types/config.js +41 -0
  34. package/dist/domain/types/config.js.map +1 -1
  35. package/dist/domain/types/index-schema.d.ts +206 -0
  36. package/dist/domain/types/index-schema.d.ts.map +1 -0
  37. package/dist/domain/types/index-schema.js +41 -0
  38. package/dist/domain/types/index-schema.js.map +1 -0
  39. package/dist/domain/types/index.d.ts +2 -0
  40. package/dist/domain/types/index.d.ts.map +1 -1
  41. package/dist/domain/types/index.js +4 -0
  42. package/dist/domain/types/index.js.map +1 -1
  43. package/dist/domain/utils/timer.d.ts +34 -0
  44. package/dist/domain/utils/timer.d.ts.map +1 -0
  45. package/dist/domain/utils/timer.js +39 -0
  46. package/dist/domain/utils/timer.js.map +1 -0
  47. package/dist/factory/create-rlm.d.ts.map +1 -1
  48. package/dist/factory/create-rlm.js +1 -0
  49. package/dist/factory/create-rlm.js.map +1 -1
  50. package/dist/infrastructure/llm/openrouter-provider.d.ts +1 -0
  51. package/dist/infrastructure/llm/openrouter-provider.d.ts.map +1 -1
  52. package/dist/infrastructure/llm/openrouter-provider.js +30 -9
  53. package/dist/infrastructure/llm/openrouter-provider.js.map +1 -1
  54. package/dist/infrastructure/llm/prompts/index.d.ts +1 -1
  55. package/dist/infrastructure/llm/prompts/index.d.ts.map +1 -1
  56. package/dist/infrastructure/llm/prompts/index.js +1 -1
  57. package/dist/infrastructure/llm/prompts/index.js.map +1 -1
  58. package/dist/infrastructure/llm/prompts/system-prompt.d.ts +14 -1
  59. package/dist/infrastructure/llm/prompts/system-prompt.d.ts.map +1 -1
  60. package/dist/infrastructure/llm/prompts/system-prompt.js +186 -52
  61. package/dist/infrastructure/llm/prompts/system-prompt.js.map +1 -1
  62. package/dist/infrastructure/logging/debug-logger.d.ts +29 -0
  63. package/dist/infrastructure/logging/debug-logger.d.ts.map +1 -0
  64. package/dist/infrastructure/logging/debug-logger.js +35 -0
  65. package/dist/infrastructure/logging/debug-logger.js.map +1 -0
  66. package/dist/infrastructure/sandbox/prelude/rlm_prelude.py +637 -41
  67. package/dist/infrastructure/sandbox/process-manager.d.ts +1 -0
  68. package/dist/infrastructure/sandbox/process-manager.d.ts.map +1 -1
  69. package/dist/infrastructure/sandbox/process-manager.js +19 -6
  70. package/dist/infrastructure/sandbox/process-manager.js.map +1 -1
  71. package/dist/infrastructure/sandbox/python-executor.d.ts +6 -2
  72. package/dist/infrastructure/sandbox/python-executor.d.ts.map +1 -1
  73. package/dist/infrastructure/sandbox/python-executor.js +138 -5
  74. package/dist/infrastructure/sandbox/python-executor.js.map +1 -1
  75. package/package.json +2 -1
  76. package/src/infrastructure/sandbox/prelude/rlm_prelude.py +637 -41
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Base system prompt for RLM
3
3
  */
4
- export declare const RLM_SYSTEM_PROMPT = "You are an RLM (Recursive Language Model) agent. Your task is to process large contexts by writing Python code that decomposes and recursively processes content.\n\n## Available Python Functions\n\nYou have access to these special functions in your Python environment:\n\n### llm_query(query: str, *context_vars) -> str\nMake a recursive LLM call with a subset of context.\n- query: The question or task to perform\n- context_vars: Variable names (strings) containing context to include\n- Returns: The LLM's response as a string\n\nExample:\n```python\n# Query with full context\nresult = llm_query(\"Summarize the main points\", \"context\")\n\n# Query with a chunk\nchunk = context[:10000]\nresult = llm_query(\"What are the key findings?\", chunk)\n```\n\n### set_result(result: str) -> None\nSet the final result of your processing. Call this when you have the answer.\n\nExample:\n```python\nset_result(\"The analysis shows three main themes: ...\")\n```\n\n### set_variable(name: str, value: str) -> None\nStore a value for use in subsequent code blocks.\n\nExample:\n```python\nset_variable(\"summary\", \"Key points from analysis...\")\n```\n\n## Available Context Variables\n\nThe following variables contain your input context:\n{context_variables}\n\n## Guidelines\n\n1. **Analyze First**: Examine the context size and structure before processing\n2. **Chunk Large Content**: If context is too large, split it into manageable chunks\n3. **Recursive Decomposition**: Use llm_query() to process chunks recursively\n4. **Aggregate Results**: Combine results from recursive calls into a coherent answer\n5. **Call set_result()**: Always call set_result() with your final answer\n\n## Example Pattern\n\n```python\n# Check if context is small enough to process directly\nif len(context) < 50000:\n result = llm_query(\"Answer the question based on this context\", \"context\")\n set_result(result)\nelse:\n # Split into chunks and process recursively\n chunk_size = len(context) // 4\n chunks = [context[i:i+chunk_size] for i in range(0, len(context), chunk_size)]\n\n results = []\n for i, chunk in enumerate(chunks):\n set_variable(f\"chunk_{i}\", chunk)\n result = llm_query(f\"Analyze chunk {i+1}/{len(chunks)}\", f\"chunk_{i}\")\n results.append(result)\n\n # Combine results\n combined = \"\\n\\n\".join(results)\n set_variable(\"combined\", combined)\n final = llm_query(\"Synthesize these partial analyses into a complete answer\", \"combined\")\n set_result(final)\n```\n\n## Current Task\n\n{query}\n\nWrite Python code to accomplish this task. Your code will be executed in a sandboxed environment.";
4
+ export declare const RLM_SYSTEM_PROMPT = "You are an RLM (Recursive Language Model) agent. Your task is to process large contexts by writing Python code that decomposes and recursively processes content.\n\n## Token Budget (CRITICAL)\n\nYour token budget per llm_query() call is approximately **125,000 tokens** (~500,000 characters).\nNEVER pass more than 500,000 characters to llm_query() - it will fail with a capacity error.\n\nUse these functions to stay within limits:\n- `count_tokens(text)` - Check token count before calling llm_query()\n- `get_units_safe(ids, max_tokens)` - Automatically fetch units within budget\n\n## CRITICAL: Examine Context First (Before Decomposing)\n\nBefore deciding how to process the context, you MUST examine it:\n\n1. **Check size and structure**:\n ```python\n print(f\"Context: {len(context)} chars, ~{len(context)//4} tokens\")\n print(context[:2000]) # See the beginning\n ```\n\n2. **Scan for patterns** relevant to the query:\n ```python\n import re\n # Find sections that might contain the answer\n matches = re.findall(r'relevant_pattern', context)\n print(f\"Found {len(matches)} potentially relevant sections\")\n ```\n\n3. **THEN decide** how to decompose:\n - Small context? Process directly\n - Found specific matches? Focus on those\n - Large, unfocused? Chunk strategically\n\nDo NOT blindly iterate through all content. Use your judgment to filter and prioritize.\n\n## Available Python Functions\n\n**ONLY USE THESE EXACT FUNCTION NAMES** - any other function will cause \"name not defined\" error.\n\n### Core Functions\n- `llm_query(query: str, *context_vars) -> str` - Make recursive LLM call (max 500K chars)\n- `llm_query_batch(queries: list[tuple[str, str]]) -> list[str]` - **PARALLEL** LLM calls (MUCH faster!)\n- `set_result(result: str) -> None` - Set final answer\n- `set_result_final(result: str, confidence: float = 1.0) -> None` - Set final answer with early termination\n- `set_variable(name: str, value: str) -> None` - Store a variable\n\n### Structure Functions (for codebase with \"=== FILE:\" markers)\n- `get_structure() -> dict` - Returns {type, total_files, units: [{id, path, tokens}]}\n- `get_unit(unit_id: str) -> str` - Get specific unit content\n- `list_units(pattern: str = None) -> list[dict]` - List units matching pattern\n- `get_units_safe(unit_ids: list, max_tokens: int = 50000) -> tuple[str, list]` - Fetch units within budget\n\n### Utility Functions\n- `count_tokens(text: str) -> int` - Estimate tokens (~4 chars/token)\n- `chunk_text(text: str, chunk_size: int = 10000, overlap: int = 500) -> list[str]` - Split text\n- `filter_lines(text: str, predicate: str) -> str` - Filter lines containing predicate\n\n**WRONG NAMES (will error):** get_repo_structure, get_file_list, list_files, get_units, get_repo_info\n\n## Available Context Variables\n\n**CRITICAL: Your code runs in a FRESH, ISOLATED Python environment.**\nThe ONLY variables available to you are:\n\n{context_variables}\n\n**DO NOT** reference any other variables - they will cause \"name not defined\" errors.\n\n## When to Use Which Approach\n\n**If context is SMALL (< 100K tokens / 400K chars):**\n- Just examine the content directly with `print(context[:5000])`\n- Answer the question with `set_result(\"your answer\")`\n- NO need for structure extraction or llm_query()\n\n**If context is LARGE and has structure (contains \"=== FILE:\" markers):**\n- Use `get_structure()` to understand the structure\n- Use `get_units_safe()` to fetch portions\n- Use `llm_query()` to analyze each portion\n\n**If context is LARGE but unstructured:**\n- Use `chunk_text()` to split into pieces\n- Use `llm_query()` on each chunk\n\n## Guidelines\n\n1. **Check Structure First**: Use get_structure() to understand content before processing\n2. **Respect Token Limits**: Always check count_tokens() before llm_query()\n3. **Use Safe Fetching**: Prefer get_units_safe() over manual chunking\n4. **Process Iteratively**: Fetch and process units in batches that fit the budget\n5. **Call set_result()**: Always call set_result() with your final answer\n\n## PERFORMANCE: Use Parallel Processing!\n\n**CRITICAL**: Use `llm_query_batch()` instead of multiple `llm_query()` calls when queries are independent.\n\n**SLOW** (sequential - each call waits for the previous):\n```python\nresults = []\nfor chunk in chunks:\n result = llm_query(\"Analyze\", chunk) # Waits for response\n results.append(result)\n```\n\n**FAST** (parallel - all calls run simultaneously):\n```python\nqueries = [(\"Analyze\", chunk) for chunk in chunks]\nresults = llm_query_batch(queries) # All run in parallel!\n```\n\nThis can provide 5-10x speedup for multi-batch processing!\n\n## Example Patterns\n\n### Small Context (< 500K chars)\n```python\nif len(context) < 400000: # Leave buffer for query\n result = llm_query(\"Answer the question\", \"context\")\n set_result(result)\n```\n\n### Codebase Analysis (FAST - using parallel batch)\n```python\n# Get structure without loading full content\nstructure = get_structure()\nprint(f\"Codebase: {structure['total_files']} files, {structure['total_tokens']} tokens\")\n\n# Get relevant files\npy_files = list_units('.py')\nunit_ids = [f['id'] for f in py_files]\n\n# Fetch in batches that fit llm_query() limit\nbatches = []\nremaining = unit_ids\nwhile remaining:\n content, remaining = get_units_safe(remaining, max_tokens=100000)\n if content:\n batches.append(content)\n\n# PARALLEL: Process all batches at once (MUCH faster than sequential!)\nqueries = [(f\"Analyze batch {i+1} of Python files\", batch) for i, batch in enumerate(batches)]\nresults = llm_query_batch(queries) # All batches processed in parallel!\n\n# Synthesize\ncombined = \"\\n\\n\".join(results)\nfinal = llm_query(\"Synthesize the analysis\", combined)\nset_result(final)\n```\n\n### Generic Large Content\n```python\nstructure = get_structure()\nall_ids = [u['id'] for u in structure['units']]\n\nresults = []\nremaining = all_ids\nwhile remaining:\n content, remaining = get_units_safe(remaining, max_tokens=100000)\n result = llm_query(\"Analyze this section\", content)\n results.append(result)\n\ncombined = \"\\n\\n\".join(results)\nfinal = llm_query(\"Provide a complete synthesis\", combined)\nset_result(final)\n```\n\n## Current Task\n\n{query}\n\nWrite Python code to accomplish this task. Your code will be executed in a sandboxed environment.";
5
5
  /**
6
6
  * Format the system prompt with context variables and query
7
7
  */
@@ -9,4 +9,17 @@ export declare function formatSystemPrompt(contextVariables: readonly {
9
9
  name: string;
10
10
  sizeHint: string;
11
11
  }[], query: string): string;
12
+ /**
13
+ * Direct answer prompt for recursive calls (depth > 0)
14
+ *
15
+ * Unlike the main RLM prompt which asks the LLM to write Python code,
16
+ * this prompt asks the LLM to provide a direct text answer.
17
+ * This matches the MIT paper's intended behavior where sub-queries
18
+ * return text answers rather than generating more code.
19
+ */
20
+ export declare const DIRECT_ANSWER_PROMPT = "You are a sub-agent analyzing content provided by a parent RLM (Recursive Language Model) agent.\n\n## Your Role\n\nYou are being called via llm_query() from a parent agent that is processing a larger context.\nYour job is to provide a DIRECT TEXT ANSWER - do NOT write Python code.\n\n## Instructions\n\n1. Read and analyze the context provided below\n2. Answer the question/task directly and thoroughly\n3. Your response will be returned as a string to the parent agent\n4. Focus on extracting and synthesizing the relevant information\n\n## Context Information\n\n{context_hint}\n\n## Task\n\n{query}\n\n## Response Format\n\nRespond with your analysis as plain text. Be comprehensive but concise.\nDo NOT wrap your response in code blocks or markdown formatting unless specifically requested.\nDo NOT write Python code - just provide the answer directly.";
21
+ /**
22
+ * Format the direct answer prompt for recursive calls
23
+ */
24
+ export declare function formatDirectAnswerPrompt(contextHint: string, query: string): string;
12
25
  //# sourceMappingURL=system-prompt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/llm/prompts/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,iBAAiB,snFAgFoE,CAAC;AAEnG;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,gBAAgB,EAAE,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAAE,EAC/D,KAAK,EAAE,MAAM,GACZ,MAAM,CAQR"}
1
+ {"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/llm/prompts/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,iBAAiB,6xMA2KoE,CAAC;AAEnG;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,gBAAgB,EAAE,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAAE,EAC/D,KAAK,EAAE,MAAM,GACZ,MAAM,CAQR;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,+1BA0B4B,CAAC;AAE9D;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,GACZ,MAAM,CAIR"}
@@ -3,78 +3,169 @@
3
3
  */
4
4
  export const RLM_SYSTEM_PROMPT = `You are an RLM (Recursive Language Model) agent. Your task is to process large contexts by writing Python code that decomposes and recursively processes content.
5
5
 
6
- ## Available Python Functions
6
+ ## Token Budget (CRITICAL)
7
7
 
8
- You have access to these special functions in your Python environment:
8
+ Your token budget per llm_query() call is approximately **125,000 tokens** (~500,000 characters).
9
+ NEVER pass more than 500,000 characters to llm_query() - it will fail with a capacity error.
9
10
 
10
- ### llm_query(query: str, *context_vars) -> str
11
- Make a recursive LLM call with a subset of context.
12
- - query: The question or task to perform
13
- - context_vars: Variable names (strings) containing context to include
14
- - Returns: The LLM's response as a string
11
+ Use these functions to stay within limits:
12
+ - \`count_tokens(text)\` - Check token count before calling llm_query()
13
+ - \`get_units_safe(ids, max_tokens)\` - Automatically fetch units within budget
15
14
 
16
- Example:
17
- \`\`\`python
18
- # Query with full context
19
- result = llm_query("Summarize the main points", "context")
15
+ ## CRITICAL: Examine Context First (Before Decomposing)
20
16
 
21
- # Query with a chunk
22
- chunk = context[:10000]
23
- result = llm_query("What are the key findings?", chunk)
24
- \`\`\`
17
+ Before deciding how to process the context, you MUST examine it:
25
18
 
26
- ### set_result(result: str) -> None
27
- Set the final result of your processing. Call this when you have the answer.
19
+ 1. **Check size and structure**:
20
+ \`\`\`python
21
+ print(f"Context: {len(context)} chars, ~{len(context)//4} tokens")
22
+ print(context[:2000]) # See the beginning
23
+ \`\`\`
28
24
 
29
- Example:
30
- \`\`\`python
31
- set_result("The analysis shows three main themes: ...")
32
- \`\`\`
25
+ 2. **Scan for patterns** relevant to the query:
26
+ \`\`\`python
27
+ import re
28
+ # Find sections that might contain the answer
29
+ matches = re.findall(r'relevant_pattern', context)
30
+ print(f"Found {len(matches)} potentially relevant sections")
31
+ \`\`\`
33
32
 
34
- ### set_variable(name: str, value: str) -> None
35
- Store a value for use in subsequent code blocks.
33
+ 3. **THEN decide** how to decompose:
34
+ - Small context? Process directly
35
+ - Found specific matches? Focus on those
36
+ - Large, unfocused? Chunk strategically
36
37
 
37
- Example:
38
- \`\`\`python
39
- set_variable("summary", "Key points from analysis...")
40
- \`\`\`
38
+ Do NOT blindly iterate through all content. Use your judgment to filter and prioritize.
39
+
40
+ ## Available Python Functions
41
+
42
+ **ONLY USE THESE EXACT FUNCTION NAMES** - any other function will cause "name not defined" error.
43
+
44
+ ### Core Functions
45
+ - \`llm_query(query: str, *context_vars) -> str\` - Make recursive LLM call (max 500K chars)
46
+ - \`llm_query_batch(queries: list[tuple[str, str]]) -> list[str]\` - **PARALLEL** LLM calls (MUCH faster!)
47
+ - \`set_result(result: str) -> None\` - Set final answer
48
+ - \`set_result_final(result: str, confidence: float = 1.0) -> None\` - Set final answer with early termination
49
+ - \`set_variable(name: str, value: str) -> None\` - Store a variable
50
+
51
+ ### Structure Functions (for codebase with "=== FILE:" markers)
52
+ - \`get_structure() -> dict\` - Returns {type, total_files, units: [{id, path, tokens}]}
53
+ - \`get_unit(unit_id: str) -> str\` - Get specific unit content
54
+ - \`list_units(pattern: str = None) -> list[dict]\` - List units matching pattern
55
+ - \`get_units_safe(unit_ids: list, max_tokens: int = 50000) -> tuple[str, list]\` - Fetch units within budget
56
+
57
+ ### Utility Functions
58
+ - \`count_tokens(text: str) -> int\` - Estimate tokens (~4 chars/token)
59
+ - \`chunk_text(text: str, chunk_size: int = 10000, overlap: int = 500) -> list[str]\` - Split text
60
+ - \`filter_lines(text: str, predicate: str) -> str\` - Filter lines containing predicate
61
+
62
+ **WRONG NAMES (will error):** get_repo_structure, get_file_list, list_files, get_units, get_repo_info
41
63
 
42
64
  ## Available Context Variables
43
65
 
44
- The following variables contain your input context:
66
+ **CRITICAL: Your code runs in a FRESH, ISOLATED Python environment.**
67
+ The ONLY variables available to you are:
68
+
45
69
  {context_variables}
46
70
 
71
+ **DO NOT** reference any other variables - they will cause "name not defined" errors.
72
+
73
+ ## When to Use Which Approach
74
+
75
+ **If context is SMALL (< 100K tokens / 400K chars):**
76
+ - Just examine the content directly with \`print(context[:5000])\`
77
+ - Answer the question with \`set_result("your answer")\`
78
+ - NO need for structure extraction or llm_query()
79
+
80
+ **If context is LARGE and has structure (contains "=== FILE:" markers):**
81
+ - Use \`get_structure()\` to understand the structure
82
+ - Use \`get_units_safe()\` to fetch portions
83
+ - Use \`llm_query()\` to analyze each portion
84
+
85
+ **If context is LARGE but unstructured:**
86
+ - Use \`chunk_text()\` to split into pieces
87
+ - Use \`llm_query()\` on each chunk
88
+
47
89
  ## Guidelines
48
90
 
49
- 1. **Analyze First**: Examine the context size and structure before processing
50
- 2. **Chunk Large Content**: If context is too large, split it into manageable chunks
51
- 3. **Recursive Decomposition**: Use llm_query() to process chunks recursively
52
- 4. **Aggregate Results**: Combine results from recursive calls into a coherent answer
91
+ 1. **Check Structure First**: Use get_structure() to understand content before processing
92
+ 2. **Respect Token Limits**: Always check count_tokens() before llm_query()
93
+ 3. **Use Safe Fetching**: Prefer get_units_safe() over manual chunking
94
+ 4. **Process Iteratively**: Fetch and process units in batches that fit the budget
53
95
  5. **Call set_result()**: Always call set_result() with your final answer
54
96
 
55
- ## Example Pattern
97
+ ## PERFORMANCE: Use Parallel Processing!
98
+
99
+ **CRITICAL**: Use \`llm_query_batch()\` instead of multiple \`llm_query()\` calls when queries are independent.
100
+
101
+ **SLOW** (sequential - each call waits for the previous):
102
+ \`\`\`python
103
+ results = []
104
+ for chunk in chunks:
105
+ result = llm_query("Analyze", chunk) # Waits for response
106
+ results.append(result)
107
+ \`\`\`
108
+
109
+ **FAST** (parallel - all calls run simultaneously):
110
+ \`\`\`python
111
+ queries = [("Analyze", chunk) for chunk in chunks]
112
+ results = llm_query_batch(queries) # All run in parallel!
113
+ \`\`\`
114
+
115
+ This can provide 5-10x speedup for multi-batch processing!
56
116
 
117
+ ## Example Patterns
118
+
119
+ ### Small Context (< 500K chars)
57
120
  \`\`\`python
58
- # Check if context is small enough to process directly
59
- if len(context) < 50000:
60
- result = llm_query("Answer the question based on this context", "context")
121
+ if len(context) < 400000: # Leave buffer for query
122
+ result = llm_query("Answer the question", "context")
61
123
  set_result(result)
62
- else:
63
- # Split into chunks and process recursively
64
- chunk_size = len(context) // 4
65
- chunks = [context[i:i+chunk_size] for i in range(0, len(context), chunk_size)]
66
-
67
- results = []
68
- for i, chunk in enumerate(chunks):
69
- set_variable(f"chunk_{i}", chunk)
70
- result = llm_query(f"Analyze chunk {i+1}/{len(chunks)}", f"chunk_{i}")
71
- results.append(result)
72
-
73
- # Combine results
74
- combined = "\\n\\n".join(results)
75
- set_variable("combined", combined)
76
- final = llm_query("Synthesize these partial analyses into a complete answer", "combined")
77
- set_result(final)
124
+ \`\`\`
125
+
126
+ ### Codebase Analysis (FAST - using parallel batch)
127
+ \`\`\`python
128
+ # Get structure without loading full content
129
+ structure = get_structure()
130
+ print(f"Codebase: {structure['total_files']} files, {structure['total_tokens']} tokens")
131
+
132
+ # Get relevant files
133
+ py_files = list_units('.py')
134
+ unit_ids = [f['id'] for f in py_files]
135
+
136
+ # Fetch in batches that fit llm_query() limit
137
+ batches = []
138
+ remaining = unit_ids
139
+ while remaining:
140
+ content, remaining = get_units_safe(remaining, max_tokens=100000)
141
+ if content:
142
+ batches.append(content)
143
+
144
+ # PARALLEL: Process all batches at once (MUCH faster than sequential!)
145
+ queries = [(f"Analyze batch {i+1} of Python files", batch) for i, batch in enumerate(batches)]
146
+ results = llm_query_batch(queries) # All batches processed in parallel!
147
+
148
+ # Synthesize
149
+ combined = "\\n\\n".join(results)
150
+ final = llm_query("Synthesize the analysis", combined)
151
+ set_result(final)
152
+ \`\`\`
153
+
154
+ ### Generic Large Content
155
+ \`\`\`python
156
+ structure = get_structure()
157
+ all_ids = [u['id'] for u in structure['units']]
158
+
159
+ results = []
160
+ remaining = all_ids
161
+ while remaining:
162
+ content, remaining = get_units_safe(remaining, max_tokens=100000)
163
+ result = llm_query("Analyze this section", content)
164
+ results.append(result)
165
+
166
+ combined = "\\n\\n".join(results)
167
+ final = llm_query("Provide a complete synthesis", combined)
168
+ set_result(final)
78
169
  \`\`\`
79
170
 
80
171
  ## Current Task
@@ -93,4 +184,47 @@ export function formatSystemPrompt(contextVariables, query) {
93
184
  .replace('{context_variables}', varList || '(none)')
94
185
  .replace('{query}', query);
95
186
  }
187
+ /**
188
+ * Direct answer prompt for recursive calls (depth > 0)
189
+ *
190
+ * Unlike the main RLM prompt which asks the LLM to write Python code,
191
+ * this prompt asks the LLM to provide a direct text answer.
192
+ * This matches the MIT paper's intended behavior where sub-queries
193
+ * return text answers rather than generating more code.
194
+ */
195
+ export const DIRECT_ANSWER_PROMPT = `You are a sub-agent analyzing content provided by a parent RLM (Recursive Language Model) agent.
196
+
197
+ ## Your Role
198
+
199
+ You are being called via llm_query() from a parent agent that is processing a larger context.
200
+ Your job is to provide a DIRECT TEXT ANSWER - do NOT write Python code.
201
+
202
+ ## Instructions
203
+
204
+ 1. Read and analyze the context provided below
205
+ 2. Answer the question/task directly and thoroughly
206
+ 3. Your response will be returned as a string to the parent agent
207
+ 4. Focus on extracting and synthesizing the relevant information
208
+
209
+ ## Context Information
210
+
211
+ {context_hint}
212
+
213
+ ## Task
214
+
215
+ {query}
216
+
217
+ ## Response Format
218
+
219
+ Respond with your analysis as plain text. Be comprehensive but concise.
220
+ Do NOT wrap your response in code blocks or markdown formatting unless specifically requested.
221
+ Do NOT write Python code - just provide the answer directly.`;
222
+ /**
223
+ * Format the direct answer prompt for recursive calls
224
+ */
225
+ export function formatDirectAnswerPrompt(contextHint, query) {
226
+ return DIRECT_ANSWER_PROMPT
227
+ .replace('{context_hint}', contextHint)
228
+ .replace('{query}', query);
229
+ }
96
230
  //# sourceMappingURL=system-prompt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../../../src/infrastructure/llm/prompts/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kGAgFiE,CAAC;AAEnG;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAChC,gBAA+D,EAC/D,KAAa;IAEb,MAAM,OAAO,GAAG,gBAAgB;SAC7B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;SAC5C,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO,iBAAiB;SACrB,OAAO,CAAC,qBAAqB,EAAE,OAAO,IAAI,QAAQ,CAAC;SACnD,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;AAC/B,CAAC"}
1
+ {"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../../../src/infrastructure/llm/prompts/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kGA2KiE,CAAC;AAEnG;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAChC,gBAA+D,EAC/D,KAAa;IAEb,MAAM,OAAO,GAAG,gBAAgB;SAC7B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;SAC5C,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO,iBAAiB;SACrB,OAAO,CAAC,qBAAqB,EAAE,OAAO,IAAI,QAAQ,CAAC;SACnD,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;6DA0ByB,CAAC;AAE9D;;GAEG;AACH,MAAM,UAAU,wBAAwB,CACtC,WAAmB,EACnB,KAAa;IAEb,OAAO,oBAAoB;SACxB,OAAO,CAAC,gBAAgB,EAAE,WAAW,CAAC;SACtC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Debug Logger Utility
3
+ *
4
+ * Provides consistent debug logging across the RLM codebase.
5
+ * Only logs when DEBUG_RLM environment variable is set.
6
+ */
7
+ /**
8
+ * Debug log level prefixes
9
+ */
10
+ export type LogPrefix = 'QH' | 'LQH' | 'LLM' | 'PE' | 'PM' | 'RLM' | 'SM';
11
+ /**
12
+ * Creates a debug logger with a specific prefix.
13
+ *
14
+ * Usage:
15
+ * ```typescript
16
+ * const dbg = createDebugLogger('QH');
17
+ * dbg('EXECUTE_START', { session: '123', depth: 0 });
18
+ * // Output: [12:34:56.789] QH: EXECUTE_START {"session":"123","depth":0}
19
+ * ```
20
+ *
21
+ * @param prefix - Short identifier for the component (e.g., 'QH' for QueryHandler)
22
+ * @returns Debug logging function that only logs when DEBUG_RLM is set
23
+ */
24
+ export declare function createDebugLogger(prefix: LogPrefix): (msg: string, data?: unknown) => void;
25
+ /**
26
+ * Check if debug mode is enabled
27
+ */
28
+ export declare function isDebugEnabled(): boolean;
29
+ //# sourceMappingURL=debug-logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug-logger.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/logging/debug-logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,MAAM,SAAS,GACjB,IAAI,GACJ,KAAK,GACL,KAAK,GACL,IAAI,GACJ,IAAI,GACJ,KAAK,GACL,IAAI,CAAC;AAET;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,GAChB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAQvC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAExC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Debug Logger Utility
3
+ *
4
+ * Provides consistent debug logging across the RLM codebase.
5
+ * Only logs when DEBUG_RLM environment variable is set.
6
+ */
7
+ /**
8
+ * Creates a debug logger with a specific prefix.
9
+ *
10
+ * Usage:
11
+ * ```typescript
12
+ * const dbg = createDebugLogger('QH');
13
+ * dbg('EXECUTE_START', { session: '123', depth: 0 });
14
+ * // Output: [12:34:56.789] QH: EXECUTE_START {"session":"123","depth":0}
15
+ * ```
16
+ *
17
+ * @param prefix - Short identifier for the component (e.g., 'QH' for QueryHandler)
18
+ * @returns Debug logging function that only logs when DEBUG_RLM is set
19
+ */
20
+ export function createDebugLogger(prefix) {
21
+ return (msg, data) => {
22
+ if (!process.env['DEBUG_RLM'])
23
+ return;
24
+ const ts = new Date().toISOString().slice(11, 23);
25
+ const dataStr = data !== undefined ? JSON.stringify(data) : '';
26
+ console.log(`[${ts}] ${prefix}: ${msg}`, dataStr);
27
+ };
28
+ }
29
+ /**
30
+ * Check if debug mode is enabled
31
+ */
32
+ export function isDebugEnabled() {
33
+ return !!process.env['DEBUG_RLM'];
34
+ }
35
+ //# sourceMappingURL=debug-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug-logger.js","sourceRoot":"","sources":["../../../src/infrastructure/logging/debug-logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAcH;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAiB;IAEjB,OAAO,CAAC,GAAW,EAAE,IAAc,EAAE,EAAE;QACrC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;YAAE,OAAO;QAEtC,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAClD,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,MAAM,KAAK,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AACpC,CAAC"}