# CrewAI Hack — Agent Guide ## Env - **Python**: `/Applications/anaconda3/envs/crewai_test/bin/python` (3.12). The `crewai_hack` env (3.14) breaks packages. - **Setup**: `setup_env.sh` — a 6-step dance (`--no-deps` for conflicting openai, litellm, OTel, phoenix). `requirements.txt` lists only 8 packages; the full env is much larger. - `.env` provides `SERPER_API_KEY` (loaded via `python-dotenv` in `crew2.py`). Duplicate key in file is harmless. ## Architecture `crew2.py` is the main pipeline. 3 crews (all local, no Modal wrapper), then 3 direct HTTP calls to Modal endpoints: 1. **Crew 1** — Corroborative web research via `SerperDevTool` 2. **Crew 2** — Opposite/complementary web research 3. **Crew 3** — Synthesize both findings → `IMAGE PROMPT:` (parsed by `startswith("IMAGE PROMPT:")`) 4. `generate_image(prompt)` → Flux LoRA (A100) 5. `generate_caption(corroborate, opposite, prompt)` → direct vLLM call → `CAPTION:` + `VOICE_STYLE:` 6. `generate_voice(style, script)` → VoxCPM TTS (T4) `app.py` is a Gradio UI entrypoint (`python app.py`). ## Commands ```sh # Run full pipeline (CLI) python crew2.py "your statement here" python crew2.py --audio /path/to/speech.wav # Run Gradio UI python app.py # Tests python -m pytest tests/ # all 47 tests python -m pytest tests/test_crew2.py # single file python -m pytest -k "test_name" # focused # Lint + format python -m ruff check . # zero warnings expected python -m ruff format . # idempotent # Deploy Modal services modal deploy vllm_inference.py # H200, slow cold start modal deploy flux_generator.py # A100 modal deploy voxcpm_generator.py # T4 modal deploy transcribe_generator.py # T4 modal deploy unsloth_finetune.py # L40S # Run Modal services standalone modal run flux_generator.py --prompt "..." --steps 30 modal run voxcpm_generator.py --text "..." --voice-style girl modal run transcribe_generator.py --audio-path /path/to/speech.wav ``` ## LLM & Observability - **LLM**: Gemma 4 26B via Modal vLLM (OpenAI-compatible). Base: `https://rcaz33--example-vllm-inference-serve.modal.run/v1` - **Two model name constants** (swap them → 404): - `_VLLM_MODEL = "openai/google/gemma-4-26B-A4B-it"` — LiteLLM prefix, for crewai `LLM()` objects - `_VLLM_SERVED_MODEL = "google/gemma-4-26B-A4B-it"` — actual vLLM served name, for raw `httpx.post()` calls - **Phoenix tracing**: configured in `crew2.py` — tries/except guarded (silently skips when pkgs not installed). Auth via `build_hf_headers()` (HF token). Endpoint: `https://RCaz-phoenix-arize-observability.hf.space/v1/traces`. Session tracking via `_using_session(session_id)`. ## Modal Services |File|App Name|GPU|Endpoint|Purpose| |---|---|---|---|---| |`vllm_inference.py`|`example-vllm-inference`|H200|`/v1/chat/completions`|Gemma 4 LLM| |`flux_generator.py`|`flux-image-generator`|A100|`POST /generate`|Flux LoRA image| |`voxcpm_generator.py`|`voxcpm-generator`|T4|`POST /synthesize`|VoxCPM TTS| |`transcribe_generator.py`|`cohere-transcriber`|T4|`POST /transcribe`|Cohere ASR| |`unsloth_finetune.py`|`example-unsloth-finetune`|L40S|—|LoRA finetuning| All use `@modal.fastapi_endpoint(method="POST")`. Call via `.get_web_url()` + `httpx.post` (NOT `.remote()`). ## Known Issues - **Serper 403**: `SERPER_API_KEY` free quota exhausted. `SerperDevTool` hangs without surfacing the error. `_run_with_timeout()` wrapper (300s clock timeout) added as mitigation. - **vLLM cold start**: H200 container scales down after 15 min idle. Next request takes 10–20 min. - **Transcription 500**: cohere-transcriber needs `modal.Secret.from_name("huggingface")` with `HF_TOKEN` set. Create via `modal secret create huggingface HF_TOKEN=hf_...`. - **Heavy ML deps not in test env**: `torch`, `diffusers`, `transformers`, `soundfile`, `librosa`, `datasets`, `voxcpm` only exist inside Modal containers. Tests mock via `sys.modules` + `@app.cls`/`@app.function` no‑op patches. - **Flux LoRA adapter**: `CodeGoat24/FLUX.2-klein-base-9B-UnifiedReward-Flex-lora` is adapter-only (no `model_index.json`). `load_lora_weights()` needs `weight_name="pytorch_lora_weights.safetensors"`. - **Modal image rebuild**: cached if hash unchanged. Add `.run_commands("echo ")` to force rebuild. ## Testing - 47 tests across 8 files in `tests/`, all pass, zero ruff warnings. - Modal service classes: tested by patching `@app.cls` + `@app.function` as identity decorators, then testing the underlying Python class directly. Heavy imports mocked via `patch.dict("sys.modules", ...)`. - Async tests use `@pytest.mark.asyncio` + `MockAsyncContextManager` for `async with`. - No `pytest.ini` or `pyproject.toml` config — vanilla pytest. ## Skills - `skills/gemma4-image-prompting.md` — photorealism, composition, lighting guidelines - `skills/semantic-clarification.md` — ambiguity detection ## Code Style - No comments in source code. Only `# ------` section headers allowed. - Crew outputs parsed by exact `startswith("KEY:")` — output format must be exact. - All Modal GPU imports happen inside `__init__` / function bodies (lazy loading). - `ruff check .` and `ruff format .` — must pass before commit.