-
Notifications
You must be signed in to change notification settings - Fork 534
feat: implement multi-provider support and session management #172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
mwaldheim
wants to merge
13
commits into
frankbria:main
from
mwaldheim:feature/multi-provider-support
Closed
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
3fbb096
feat: implement multi-provider support and session management
mwaldheim b37b518
fix: sanitize RALPH_PROVIDER to prevent path traversal
mwaldheim 698e5e2
fix: restrict allowed tools to specific patterns in Claude provider
mwaldheim 6d66c37
fix: prevent global mutation of CLAUDE_OUTPUT_FORMAT in live mode
mwaldheim 84dbbfe
fix: use jq for robust JSON construction in PROGRESS_FILE
mwaldheim 7149c0e
fix: improve session age handling and test suite compatibility
mwaldheim de21ae5
fix: correctly join multi-line Copilot command invocation
mwaldheim b27cd2e
fix: log warning for unsupported live mode in Gemini provider
mwaldheim 1a54b9a
fix: use array for Gemini session arguments to improve robustness
mwaldheim 76f86d8
fix: use real newlines in Gemini prompt construction
mwaldheim c31608e
refactor: extract build_loop_context to shared base provider
mwaldheim 2d5be90
fix: explicitly pass loop_count to reset_session for accurate session…
mwaldheim ebcce88
fix: improve provider robustness and session logging safety
mwaldheim File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| # Analysis: Supporting Gemini & GitHub Copilot in Ralph | ||
|
|
||
| ## 1. Current Architecture vs. Required Architecture | ||
|
|
||
| Currently, **Ralph is a Supervisor**, not an Agent. | ||
| * **Ralph's Job:** It watches `claude-code`, ensures it doesn't get stuck, manages its rate limits, and decides when to stop. | ||
| * **Claude Code's Job:** It acts as the **Agent**. It reads files, thinks, decides to edit a file, runs `sed`/`git`, checks the result, and iterates. | ||
|
|
||
| **The "Agent Gap":** | ||
| Standard CLIs for Gemini (`gemini`) and GitHub Copilot (`gh copilot`) are primarily **Text-In/Text-Out** interfaces. They generate code snippets or explanations but **do not** natively execute tools (like writing files or running tests) in a continuous loop on your terminal. | ||
|
|
||
| To support them, Ralph cannot just "wrap" them. Ralph must **become the Agent Runtime**. | ||
|
|
||
| ## 2. Transformation Required | ||
|
|
||
| To support non-agentic CLIs (like Gemini/Copilot), Ralph must expand its responsibilities: | ||
|
|
||
| | Feature | Current (Claude Code) | Proposed (Gemini/Copilot) | | ||
| | :--- | :--- | :--- | | ||
| | **Thinking** | Claude Code | Gemini / Copilot | | ||
| | **Tool Execution** | **Claude Code** | **Ralph (New)** | | ||
| | **Loop Management** | Ralph | Ralph | | ||
| | **State/Memory** | Claude Code | Ralph (New) | | ||
|
|
||
| ## 3. Key Components to Build | ||
|
|
||
| ### A. Provider Abstraction Layer (`lib/providers/`) | ||
| We need a standard interface for interacting with different AIs. | ||
| * `init_session()`: Start a conversation. | ||
| * `send_message(prompt, context)`: Send user input + file context. | ||
| * `parse_response(output)`: Extract text content AND **Tool Calls**. | ||
|
|
||
| ### B. Tool Execution Engine (`lib/tool_executor.sh`) | ||
| Since Gemini/Copilot won't edit files themselves, Ralph must do it. | ||
| * **Protocol:** Define a format for the AI to request actions (e.g., XML tags like `<write_file path="...">content</write_file>` or JSON function calls). | ||
| * **Executor:** A script that parses these requests and runs: | ||
| * `write_file`: create/update files. | ||
| * `run_command`: execute bash commands (with safety checks). | ||
| * `read_file`: read file content to feed back to the AI. | ||
|
|
||
| ### C. Prompt Engineering (`templates/prompts/`) | ||
| * **Claude:** Uses its built-in system prompt. | ||
| * **Gemini/Copilot:** We must inject a **System Prompt** that teaches them: | ||
| * "You are an autonomous coding agent." | ||
| * "You have access to these tools: read_file, write_file..." | ||
| * "To use a tool, output this specific format..." | ||
|
|
||
| ## 4. Implementation Steps | ||
|
|
||
| 1. **Refactor `ralph_loop.sh`**: | ||
| * Replace direct `claude` calls with `provider.send_message`. | ||
| * Add a check: Does the provider handle tools? | ||
| * **Yes (Claude):** Do nothing (current behavior). | ||
| * **No (Gemini):** Parse output -> Run `ToolExecutor` -> Feed result back to `provider.send_message`. | ||
|
|
||
| 2. **Create Provider Adapters**: | ||
| * `lib/providers/claude.sh`: Wraps existing logic. | ||
| * `lib/providers/gemini.sh`: Wraps `gemini-cli` (or API curl calls), handles JSON parsing. | ||
| * `lib/providers/copilot.sh`: Wraps `gh copilot suggest` (more complex due to interactive nature, might need `expect` or API usage). | ||
|
|
||
| 3. **Build the Runtime**: | ||
| * Implement `lib/tool_executor.sh`. | ||
| * Implement "Tool Feedback Loop" in `ralph_loop.sh`. | ||
|
|
||
| ## 5. Conclusion | ||
| Making Ralph compatible with Gemini/Copilot is a **major architectural upgrade**. It moves Ralph from being a "Process Manager" to being a "ReAct (Reason+Act) Agent Framework". |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| # Implementation Plan: Multi-Provider Support | ||
|
|
||
| This plan outlines the steps to transform Ralph into a multi-provider agent framework. | ||
|
|
||
| ## Phase 1: Modularization (The "Socket") | ||
| **Goal:** Abstract the hardcoded `claude` commands into a plugin system. | ||
|
|
||
| - [ ] **Create `lib/providers/` directory.** | ||
| - [ ] **Create `lib/providers/base.sh`:** Define the interface (functions `provider_init`, `provider_chat`, `provider_parse`). | ||
| - [ ] **Create `lib/providers/claude.sh`:** Move current `claude` CLI logic here. | ||
| - [ ] **Update `ralph_loop.sh`**: | ||
| - Load the selected provider script based on `RALPH_PROVIDER` env var. | ||
| - Replace `execute_claude_code` with generic `execute_provider_loop`. | ||
|
|
||
| ## Phase 2: The Agent Runtime (The "Brain") | ||
| **Goal:** Enable Ralph to execute tools for "dumb" LLMs. | ||
|
|
||
| - [ ] **Design Tool Protocol:** Define how LLMs should request actions (e.g., Markdown code blocks or XML). | ||
| - Example: | ||
| ```xml | ||
| <tool name="write_file"> | ||
| <path>src/main.py</path> | ||
| <content>print("hello")</content> | ||
| </tool> | ||
| ``` | ||
| - [ ] **Create `lib/tool_executor.sh`:** | ||
| - Function `extract_tool_calls(llm_output)` | ||
| - Function `execute_tool(name, args)` | ||
| - Safety checks (prevent `rm -rf /`). | ||
| - [ ] **Create `templates/system_prompts/generic_agent.md`:** | ||
| - A master prompt that explains available tools and the output format to the LLM. | ||
|
|
||
| ## Phase 3: Gemini Adapter | ||
| **Goal:** Connect Google Gemini. | ||
|
|
||
| - [ ] **Prerequisite:** Install `gemini` CLI or use `curl` with API Key. | ||
| - [ ] **Create `lib/providers/gemini.sh`:** | ||
| - Implement `provider_chat`: | ||
| - Construct payload with `templates/system_prompts/generic_agent.md` + User Prompt + History. | ||
| - Call API. | ||
| - Implement `provider_parse`: | ||
| - Extract text. | ||
| - Detect if tool calls are present. | ||
|
|
||
| ## Phase 4: GitHub Copilot Adapter | ||
| **Goal:** Connect GitHub Copilot. | ||
|
|
||
| - [ ] **Investigation:** Determine best CLI entry point (`gh copilot` vs raw API). | ||
| - [ ] **Create `lib/providers/copilot.sh`:** | ||
| - Similar adaptation as Gemini. | ||
| - *Note:* Copilot often refuses "system prompts" in CLI. May require "User" role spoofing. | ||
|
|
||
| ## Phase 5: Configuration Update | ||
| **Goal:** User-friendly switching. | ||
|
|
||
| - [ ] **Update `ralph-setup` / `ralph-enable`:** | ||
| - Ask "Which AI provider do you want to use?" | ||
| - Generate `.ralphrc` with `RALPH_PROVIDER=gemini`. | ||
| - [ ] **Update `.ralphrc` template:** Add provider configuration sections. | ||
|
|
||
| ## Estimated Effort | ||
| - **Phase 1:** 2 days (Refactoring) | ||
| - **Phase 2:** 3-4 days (Security & Logic) | ||
| - **Phase 3:** 1-2 days (Integration) | ||
| - **Total:** ~1-2 weeks for a robust MVP. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| #!/bin/bash | ||
| # Base Provider Loader for Ralph | ||
|
|
||
| # Load the configured provider | ||
| load_provider() { | ||
| local raw_provider="${RALPH_PROVIDER:-claude}" | ||
|
|
||
| # Sanitize and validate provider name to prevent path traversal | ||
| if [[ ! "$raw_provider" =~ ^[a-zA-Z0-9_-]+$ ]]; then | ||
| log_status "ERROR" "Invalid AI provider name: $raw_provider (only alphanumeric, underscores, and hyphens allowed)" | ||
| exit 1 | ||
| fi | ||
|
|
||
| local provider_name="$raw_provider" | ||
| local provider_script="$RALPH_HOME/lib/providers/${provider_name}.sh" | ||
|
|
||
| # Fallback to local path if RALPH_HOME not set or script not found | ||
| if [[ ! -f "$provider_script" ]]; then | ||
| provider_script="$(dirname "${BASH_SOURCE[0]}")/${provider_name}.sh" | ||
| fi | ||
|
|
||
| if [[ -f "$provider_script" ]]; then | ||
| source "$provider_script" | ||
| log_status "INFO" "Loaded AI provider: $provider_name" | ||
| else | ||
| log_status "ERROR" "AI provider script not found: $provider_script" | ||
| exit 1 | ||
| fi | ||
| } | ||
|
|
||
| # Helper to build loop context shared by all providers | ||
| build_loop_context() { | ||
| local loop_count=$1 | ||
| local context="Loop #$loop_count. " | ||
|
|
||
| if [[ -f "$RALPH_DIR/fix_plan.md" ]]; then | ||
| local incomplete_tasks=$(grep -cE "^[[:space:]]*- \[ \]" "$RALPH_DIR/fix_plan.md" 2>/dev/null || true) | ||
| context+="Remaining tasks: ${incomplete_tasks:-0}. " | ||
| fi | ||
|
|
||
| if [[ -f "$RALPH_DIR/.circuit_breaker_state" ]]; then | ||
| local cb_state=$(jq -r '.state // "UNKNOWN"' "$RALPH_DIR/.circuit_breaker_state" 2>/dev/null) | ||
| [[ "$cb_state" != "CLOSED" && -n "$cb_state" && "$cb_state" != "null" ]] && context+="Circuit breaker: $cb_state. " | ||
| fi | ||
|
|
||
| if [[ -f "$RALPH_DIR/.response_analysis" ]]; then | ||
| local prev_summary=$(jq -r '.analysis.work_summary // ""' "$RALPH_DIR/.response_analysis" 2>/dev/null | head -c 200) | ||
| [[ -n "$prev_summary" && "$prev_summary" != "null" ]] && context+="Previous: $prev_summary" | ||
| fi | ||
|
|
||
| echo "${context:0:500}" | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.