diff --git a/dyno/.gitignore b/dyno/.gitignore index 454fca2..dc847b7 100644 --- a/dyno/.gitignore +++ b/dyno/.gitignore @@ -10,3 +10,6 @@ workspace/ # OS .DS_Store Thumbs.db +gtext/ +.git/ +.gtree diff --git a/dyno/Dockerfile b/dyno/Dockerfile index d4e296a..95bd609 100644 --- a/dyno/Dockerfile +++ b/dyno/Dockerfile @@ -8,10 +8,17 @@ RUN apk add --no-cache \ bash \ git \ curl \ - go \ musl-dev \ gcc +# Install newer Go version +RUN curl -fsSL https://go.dev/dl/go1.25.7.linux-amd64.tar.gz -o /tmp/go.tar.gz && \ + rm -rf /usr/local/go && \ + tar -C /usr/local -xzf /tmp/go.tar.gz && \ + rm /tmp/go.tar.gz + +ENV PATH=/usr/local/go/bin:$PATH + # Build godex from source (build context is project root) WORKDIR /build COPY go.mod go.sum ./ @@ -37,18 +44,19 @@ RUN apk add --no-cache \ jq # Install mock HTTP server dependencies -RUN pip3 install --no-cache-dir flask requests +RUN pip3 install --no-cache-dir --break-system-packages flask requests # Create directories -RUN mkdir -p /dyno/logs /dyno/workspace /dyno/mocks +RUN mkdir -p /dyno/logs /dyno/workspace /dyno/mocks /root/.godex # Copy built binary COPY --from=builder /build/godex /usr/local/bin/godex -# Copy dyno scripts -COPY dyno-mock-server.py /dyno/dyno-mock-server.py -COPY dyno-entrypoint.sh /dyno/dyno-entrypoint.sh -COPY dyno.env /dyno/dyno.env +# Copy dyno scripts (from dyno/ folder in build context) +COPY dyno/dyno-mock-server.py /dyno/dyno-mock-server.py +COPY dyno/dyno-entrypoint.sh /dyno/dyno-entrypoint.sh +COPY dyno/dyno.env /dyno/dyno.env +COPY dyno/dummy-provider.yaml /root/.godex/providers.yaml RUN chmod +x /dyno/*.sh /dyno/dyno-mock-server.py @@ -61,6 +69,8 @@ ENV DYNO_MOCK_HTTP=true ENV DYNO_MOCK_FILEOPS=true ENV DYNO_MOCK_COMMANDS=true ENV DYNO_TRACKING_ENABLED=true +ENV CODEX_CONFIG_PATH=/root/.godex/providers.yaml +ENV CODEX_PROVIDER_NAME=dyno-mock # Volume for outputs VOLUME ["/dyno/logs", "/dyno/workspace"] diff --git a/dyno/dummy-provider.yaml b/dyno/dummy-provider.yaml new file mode 100644 index 0000000..2dfa936 --- /dev/null +++ b/dyno/dummy-provider.yaml @@ -0,0 +1,10 @@ +providers: + - name: dyno-mock + type: ollama + endpoint: http://localhost:8888/v1 + model: gpt-4 + api_key: dummy-key-for-testing +default_provider: dyno-mock +vision_enabled: true +vision_cli_downloaded: true + diff --git a/dyno/dyno-mock-server.py b/dyno/dyno-mock-server.py index c11f90c..617ab24 100644 --- a/dyno/dyno-mock-server.py +++ b/dyno/dyno-mock-server.py @@ -3,10 +3,7 @@ DYNO Mock Server - Intercepts all HTTP requests and returns mock responses to simulate tool calling for godex testing. -This server: -- Logs all HTTP requests to tracking files -- Returns dummy responses that simulate AI tool calling -- Supports OpenAI-compatible API responses +Handles both OpenAI and Ollama API formats. """ import os @@ -63,103 +60,102 @@ class MockHandler(BaseHTTPRequestHandler): def get_mock_response(self, method, path, headers=None, body=None): """Determine mock response based on request""" - # Check for tool result in request - if body: - try: - req_data = json.loads(body) - if 'messages' in req_data: - last_msg = req_data['messages'][-1] if req_data['messages'] else {} - if last_msg.get('role') == 'tool': - return self.mock_tool_result_response(last_msg) - except: - pass + # Ollama endpoints + if '/api/chat' in path: + return self.mock_ollama_chat(body) + if '/api/show' in path: + return self.mock_ollama_show(body) + if '/api/generate' in path: + return self.mock_ollama_generate(body) + if '/api/tags' in path or '/tags' in path: + return self.mock_ollama_tags() - # OpenAI/ChatGPT style endpoints + # OpenAI endpoints if '/v1/chat/completions' in path or 'chat/completions' in path: return self.mock_chat_completion(body) - - # OpenAI embeddings if '/v1/embeddings' in path or 'embeddings' in path: return self.mock_embeddings_response() - # Anthropic Claude - if 'anthropic.com' in path or '/v1/messages' in path: - return self.mock_anthropic_response(body) - # Default: generic success response return self.mock_default_response() - def mock_tool_result_response(self, tool_msg): - """Return mock response after tool execution""" - tool_name = tool_msg.get('name', 'unknown') - content = tool_msg.get('content', '') - - logger.info(f"Mocking tool result for: {tool_name}") - - response = { - "choices": [{ - "index": 0, - "message": { - "role": "assistant", - "content": f"[Tool {tool_name} executed successfully. Result: {content[:200]}]" - }, - "finish_reason": "stop" - }], - "usage": { - "prompt_tokens": 100, - "completion_tokens": 50, - "total_tokens": 150 - } - } - return 200, response - - def mock_chat_completion(self, body): - """Mock OpenAI chat completion - simulates tool calling""" + def mock_ollama_chat(self, body): + """Mock Ollama chat response""" try: req_data = json.loads(body) if body else {} except: req_data = {} - # Check if this is a tool call request - tools = req_data.get('tools', []) + model = req_data.get('model', 'gpt-4') messages = req_data.get('messages', []) - # If tools provided and no tool call yet, simulate a tool call - if tools and not any(m.get('tool_calls', []) for m in messages if isinstance(m, dict)): - # Simulate tool calling behavior - tool_name = tools[0].get('function', {}).get('name', 'example_tool') - - response = { - "id": "chatcmpl-mock-dyno-001", - "object": "chat.completion", - "created": int(datetime.now().timestamp()), - "model": req_data.get('model', 'gpt-4'), - "choices": [{ - "index": 0, - "message": { - "role": "assistant", - "tool_calls": [{ - "id": "call_mock_001", - "type": "function", - "function": { - "name": tool_name, - "arguments": json.dumps({"query": "mocked query for testing"}) - } - }] - }, - "finish_reason": "tool_calls" - }], - "usage": { - "prompt_tokens": 50, - "completion_tokens": 30, - "total_tokens": 80 - } - } - return 200, response - - # Regular chat response + # Build a response response = { - "id": "chatcmpl-mock-dyno-002", + "model": model, + "message": { + "role": "assistant", + "content": "[DYNO MOCK] This is a simulated AI response. All network calls are being tracked and mocked for testing purposes." + }, + "done": True + } + return 200, response + + def mock_ollama_show(self, body): + """Mock Ollama show response""" + try: + req_data = json.loads(body) if body else {} + except: + req_data = {} + + model = req_data.get('name', 'gpt-4') + + response = { + "model": model, + "modified_at": "2026-01-01T00:00:00Z", + "size": 0, + "digest": "mockdigest123" + } + return 200, response + + def mock_ollama_generate(self, body): + """Mock Ollama generate response""" + try: + req_data = json.loads(body) if body else {} + except: + req_data = {} + + model = req_data.get('model', 'gpt-4') + + response = { + "model": model, + "response": "[DYNO MOCK] This is a simulated AI response.", + "done": True + } + return 200, response + + def mock_ollama_tags(self): + """Mock Ollama tags response""" + response = { + "models": [ + { + "name": "gpt-4", + "modified_at": "2026-01-01T00:00:00Z", + "size": 0, + "digest": "mockdigest123" + } + ] + } + return 200, response + + def mock_chat_completion(self, body): + """Mock OpenAI chat completion""" + try: + req_data = json.loads(body) if body else {} + except: + req_data = {} + + response = { + "id": "chatcmpl-mock-dyno-001", "object": "chat.completion", "created": int(datetime.now().timestamp()), "model": req_data.get('model', 'gpt-4'), @@ -167,7 +163,7 @@ class MockHandler(BaseHTTPRequestHandler): "index": 0, "message": { "role": "assistant", - "content": "[DYNO MOCK RESPONSE] This is a simulated AI response. All network calls are being tracked and mocked for testing purposes." + "content": "[DYNO MOCK] This is a simulated AI response." }, "finish_reason": "stop" }], @@ -196,25 +192,6 @@ class MockHandler(BaseHTTPRequestHandler): } return 200, response - def mock_anthropic_response(self, body): - """Mock Anthropic Claude response""" - response = { - "id": "msg_mock_001", - "type": "message", - "role": "assistant", - "content": [{ - "type": "text", - "text": "[DYNO MOCK RESPONSE] This is a simulated Claude response. All network calls are being tracked and mocked." - }], - "model": "claude-3-sonnet-20240229", - "stop_reason": "end_turn", - "usage": { - "input_tokens": 50, - "output_tokens": 50 - } - } - return 200, response - def mock_default_response(self): """Default mock response""" response = { diff --git a/dyno/dyno-run.sh b/dyno/dyno-run.sh index 3d8cb4a..0df0d48 100755 --- a/dyno/dyno-run.sh +++ b/dyno/dyno-run.sh @@ -14,12 +14,12 @@ log_error() { echo "[DYNO] ERROR: $1"; } build_dyno_image() { log_info "Building DYNO Docker image..." cd "$PROJECT_ROOT" -build_dyno_image() { - log_info "Building DYNO Docker image..." - cd "$SCRIPT_DIR" - docker build -t "$DOCKER_IMAGE" . + docker build --no-cache -t "$DOCKER_IMAGE" -f "$SCRIPT_DIR/Dockerfile" . log_success "Built Docker image: $DOCKER_IMAGE" } + +run_dyno() { + local args="$@" log_info "Running godex in isolated DYNO container..." log_info "Arguments: $args" @@ -39,11 +39,8 @@ build_dyno_image() { -e DYNO_TRACKING_ENABLED=true \ -e DYNO_MOCK_HTTP=true \ -e DYNO_MOCK_FILEOPS=true \ - -e DYNO_MOCK_COMMANDS=true \ - -e DYNO_LOG_DIR=/dyno/logs \ -e GODEX_ARGS="$args" \ - "$DOCKER_IMAGE" \ - godex $args + "$DOCKER_IMAGE" log_success "DYNO execution completed" } @@ -61,13 +58,13 @@ show_logs() { main() { local cmd="${1:-run}" - shift || true - + case "$cmd" in build) build_dyno_image ;; run) + shift run_dyno "$@" ;; logs)