An ML intern that autonomously researches, writes, and ships good quality ML related code using the Hugging Face ecosystem β with deep access to docs, papers, datasets, and cloud compute.
git clone git@github.com:huggingface/ml-intern.git
cd ml-intern
uv sync
uv tool install -e .ml-internCreate a .env file in the project root (or export these in your shell):
ANTHROPIC_API_KEY=<your-anthropic-api-key> # if using anthropic models
OPENAI_API_KEY=<your-openai-api-key> # if using openai models
DEEPSEEK_API_KEY=<your-deepseek-api-key> # if using deepseek models
HF_TOKEN=<your-hugging-face-token>
GITHUB_TOKEN=<github-personal-access-token> If no HF_TOKEN is set, the CLI will prompt you to paste one on first launch. To get a GITHUB_TOKEN follow the tutorial here.
Interactive mode (start a chat session):
ml-internHeadless mode (single prompt, auto-approve):
ml-intern "fine-tune llama on my dataset"Options:
ml-intern --model anthropic/claude-opus-4-6 "your prompt"
ml-intern --model openai/gpt-5.5 "your prompt"
ml-intern --model deepseek/deepseek-v4-pro "your prompt"
ml-intern --max-iterations 100 "your prompt"
ml-intern --no-stream "your prompt"Every session is auto-uploaded to your own private Hugging Face dataset in Claude Code JSONL format, which the HF Agent Trace Viewer auto-detects so you can browse turns, tool calls, and model responses directly on the Hub.
By default the dataset is named {your-hf-username}/ml-intern-sessions and is
created private. You can flip it to public from inside the CLI:
/share-traces # show current visibility + dataset URL
/share-traces public # publish (anyone can view)
/share-traces private # lock it back downYou can also flip visibility from the dataset page on huggingface.co β the agent honours whatever you set there for subsequent uploads.
To opt out entirely, set in your CLI config (e.g. configs/cli_agent_config.json
or ~/.config/ml-intern/cli_agent_config.json):
{ "share_traces": false }To override the destination repo, set:
{ "personal_trace_repo_template": "{hf_user}/my-custom-traces" }The shared smolagents/ml-intern-sessions dataset is unrelated and only
receives anonymized telemetry rows used by the backend KPI scheduler.
ML Intern currently supports one-way notification gateways from CLI sessions. These gateways send out-of-band status updates; they do not accept inbound chat messages.
Slack notifications use the Slack Web API to post messages when the agent needs
approval, hits an error, or completes a turn. Create a Slack app with a bot token
that has chat:write, invite the bot to the target channel, then set:
SLACK_BOT_TOKEN=xoxb-...
SLACK_CHANNEL_ID=C...The CLI automatically creates a slack.default destination when both variables
are present. Optional environment variables for the env-only default:
ML_INTERN_SLACK_NOTIFICATIONS=false
ML_INTERN_SLACK_DESTINATION=slack.ops
ML_INTERN_SLACK_AUTO_EVENTS=approval_required,error,turn_complete
ML_INTERN_SLACK_ALLOW_AGENT_TOOL=true
ML_INTERN_SLACK_ALLOW_AUTO_EVENTS=trueFor a persistent user-level config, put overrides in
~/.config/ml-intern/cli_agent_config.json or point ML_INTERN_CLI_CONFIG at a
JSON file:
{
"messaging": {
"enabled": true,
"auto_event_types": ["approval_required", "error", "turn_complete"],
"destinations": {
"slack.ops": {
"provider": "slack",
"token": "${SLACK_BOT_TOKEN}",
"channel": "${SLACK_CHANNEL_ID}",
"allow_agent_tool": true,
"allow_auto_events": true
}
}
}
}Route all LLM calls through your own LiteLLM proxy or gateway by setting:
export LITELLM_API_BASE="http://localhost:4000"
export LITELLM_API_KEY="sk-your-gateway-key"When LITELLM_API_BASE is set, every model β including anthropic/, openai/,
deepseek/, bedrock/, and HF Router IDs β is routed through that endpoint
with LITELLM_API_KEY as the auth token. The gateway receives the model name
with its provider prefix (e.g. openai/gpt-5.5) and handles upstream routing.
Provider-specific parameters like reasoning effort and thinking config are
forwarded as-is.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β User/CLI β
ββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββ¬βββββββββββ
β Operations β Events
β (user_input, exec_approval, β
submission_queue interrupt, compact, ...) event_queue
β β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β submission_loop (agent_loop.py) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β 1. Receive Operation from queue β β β
β β 2. Route to handler (run_agent/compact/...) β β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β Handlers.run_agent() β ββββ€
β β β β β
β β ββββββββββββββββββββββββββββββββββββββββββ β β β
β β β Agentic Loop (max 300 iterations) β β β β
β β β β β β β
β β β ββββββββββββββββββββββββββββββββββββ β β β β
β β β β Session β β β β β
β β β β ββββββββββββββββββββββββββββββ β β β β β
β β β β β ContextManager β β β β β β
β β β β β β’ Message history β β β β β β
β β β β β (litellm.Message[]) β β β β β β
β β β β β β’ Auto-compaction (170k) β β β β β β
β β β β β β’ Session upload to HF β β β β β β
β β β β ββββββββββββββββββββββββββββββ β β β β β
β β β β β β β β β
β β β β ββββββββββββββββββββββββββββββ β β β β β
β β β β β ToolRouter β β β β β β
β β β β β ββ HF docs & research β β β β β β
β β β β β ββ HF repos, datasets, β β β β β β
β β β β β β jobs, papers β β β β β β
β β β β β ββ GitHub code search β β β β β β
β β β β β ββ Sandbox & local tools β β β β β β
β β β β β ββ Planning β β β β β β
β β β β β ββ MCP server tools β β β β β β
β β β β ββββββββββββββββββββββββββββββ β β β β β
β β β ββββββββββββββββββββββββββββββββββββ β β β β
β β β β β β β
β β β ββββββββββββββββββββββββββββββββββββ β β β β
β β β β Doom Loop Detector β β β β β
β β β β β’ Detects repeated tool patterns β β β β β
β β β β β’ Injects corrective prompts β β β β β
β β β ββββββββββββββββββββββββββββββββββββ β β β β
β β β β β β β
β β β Loop: β β β β
β β β 1. LLM call (litellm.acompletion) β β β β
β β β β β β β β
β β β 2. Parse tool_calls[] β β β β
β β β β β β β β
β β β 3. Approval check β β β β
β β β (jobs, sandbox, destructive ops) β β β β
β β β β β β β β
β β β 4. Execute via ToolRouter β β β β
β β β β β β β β
β β β 5. Add results to ContextManager β β β β
β β β β β β β β
β β β 6. Repeat if tool_calls exist β β β β
β β ββββββββββββββββββββββββββββββββββββββββββ β β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ΄βββ
User Message
β
[Add to ContextManager]
β
βββββββββββββββββββββββββββββββββββββββββββββ
β Iteration Loop (max 300) β
β β
β Get messages + tool specs β
β β β
β litellm.acompletion() β
β β β
β Has tool_calls? ββNoββ> Done β
β β β
β Yes β
β β β
β Add assistant msg (with tool_calls) β
β β β
β Doom loop check β
β β β
β For each tool_call: β
β β’ Needs approval? ββYesββ> Wait for β
β β user confirm β
β No β
β β β
β β’ ToolRouter.execute_tool() β
β β’ Add result to ContextManager β
β β β
β Continue loop ββββββββββββββββββ β
β β β β
β βββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββ
βββ agent/ # Core agent logic (Python package)
β βββ main.py # CLI entry point
β βββ config.py # Config loading, env var substitution
β βββ core/ # Agent engine
β β βββ agent_loop.py # Main agent loop (submission, LLM, tools)
β β βββ session.py # Session, Event, ContextManager classes
β β βββ tools.py # ToolRouter, ToolSpec, built-in tool registry
β β βββ llm_params.py # LiteLLM kwargs resolution per provider
β β βββ model_switcher.py # CLI /model command + probe orchestration
β β βββ effort_probe.py # Reasoning effort probe cascade
β β βββ doom_loop.py # Doom loop detection
β β βββ approval_policy.py # Approval rules for sensitive operations
β β βββ cost_estimation.py # Cost estimation for tool operations
β β βββ prompt_caching.py # Anthropic prompt caching breakpoints
β β βββ hf_access.py # HF API access helpers
β β βββ hf_router_catalog.py # HF Router model catalog pre-warming
β β βββ hf_tokens.py # Token resolution (env, HF CLI, etc.)
β β βββ redact.py # Sensitive data redaction
β β βββ session_persistence.py # Session save/load to MongoDB/file
β β βββ session_uploader.py # Upload to HF datasets
β β βββ telemetry.py # Heartbeat saving, telemetry
β βββ tools/ # Tool implementations
β β βββ docs_tools.py # HF documentation search/fetch
β β βββ papers_tool.py # Academic paper search
β β βββ jobs_tool.py # HF Jobs compute
β β βββ sandbox_tool.py # Sandbox environment management
β β βββ sandbox_client.py # Remote sandbox client
β β βββ dataset_tools.py # Dataset inspection
β β βββ research_tool.py # Sub-agent research
β β βββ plan_tool.py # Task planning
β β βββ github_find_examples.py # GitHub code search
β β βββ github_list_repos.py # GitHub repo listing
β β βββ github_read_file.py # GitHub file reading
β β βββ hf_repo_files_tool.py # HF repo file operations
β β βββ hf_repo_git_tool.py # HF repo git operations
β β βββ notify_tool.py # Slack notifications
β β βββ web_search_tool.py # Web search
β β βββ local_tools.py # Local shell/file operations
β βββ context_manager/ # Context window compaction
β β βββ manager.py
β βββ messaging/ # Notification gateway (Slack)
β β βββ gateway.py
β β βββ slack.py
β β βββ models.py
β βββ prompts/ # System prompts (YAML)
β βββ sft/ # SFT dataset building
β βββ tagger.py
βββ backend/ # FastAPI web backend
β βββ main.py # App setup, CORS, static files
β βββ session_manager.py # Multi-session management, SSE broadcasting
β βββ dependencies.py # Auth dependency injection
β βββ user_quotas.py # Per-user rate limiting
β βββ kpis_scheduler.py # In-process KPI rollup scheduler
β βββ models.py # Pydantic API models
β βββ start.sh # Production start script
β βββ routes/
β βββ agent.py # Agent SSE/WS/REST endpoints
β βββ auth.py # HF OAuth login/callback
βββ frontend/ # React SPA
β βββ src/
β β βββ components/
β β β βββ Chat/ # Chat message components
β β β βββ CodePanel/ # Code editing panel
β β β βββ Layout/ # App shell layout
β β β βββ SessionSidebar/ # Session list sidebar
β β β βββ WelcomeScreen/ # Landing/welcome screen
β β β βββ SessionChat.tsx # Main chat session view
β β β βββ ClaudeCapDialog.tsx # Claude capability dialog
β β β βββ JobsUpgradeDialog.tsx # Jobs hardware upgrade dialog
β β β βββ YoloControl.tsx # YOLO mode toggle
β β βββ hooks/ # React hooks
β β β βββ useAgentChat.ts # AI SDK chat hook (SSE transport)
β β β βββ useAuth.ts # Auth state hook
β β β βββ useUserQuota.ts # Quota tracking hook
β β βββ store/ # Zustand stores
β β β βββ agentStore.ts # Agent state
β β β βββ sessionStore.ts # Session management
β β β βββ layoutStore.ts # UI layout state
β β βββ lib/ # Client libraries
β β β βββ sse-chat-transport.ts # SSE transport for AI SDK
β β β βββ chat-message-store.ts # Client-side message store
β β βββ types/ # TypeScript type definitions
β β βββ utils/ # Shared utilities
β βββ vite.config.ts # Vite config with API proxy
β βββ package.json
βββ configs/ # JSON config files
β βββ cli_agent_config.json # CLI defaults
β βββ frontend_agent_config.json # Web UI defaults
βββ scripts/ # Utility scripts
β βββ build_kpis.py
β βββ build_sft.py
β βββ sweep_orphan_sandboxes.py
βββ tests/
β βββ unit/ # 30 unit test files
β βββ integration/ # 2 integration test files
βββ Dockerfile # Multi-stage Docker build
βββ pyproject.toml # Python project metadata and dependencies
βββ README.md
The agent emits the following events via event_queue:
processing- Starting to process user inputready- Agent is ready for inputassistant_chunk- Streaming token chunkassistant_message- Complete LLM response textassistant_stream_end- Token stream finishedtool_call- Tool being called with argumentstool_output- Tool execution resulttool_log- Informational tool log messagetool_state_change- Tool execution state transitionapproval_required- Requesting user approval for sensitive operationsturn_complete- Agent finished processingerror- Error occurred during processinginterrupted- Agent was interruptedcompacted- Context was compactedundo_complete- Undo operation completedshutdown- Agent shutting down
Edit agent/core/tools.py:
def create_builtin_tools() -> list[ToolSpec]:
return [
ToolSpec(
name="your_tool",
description="What your tool does",
parameters={
"type": "object",
"properties": {
"param": {"type": "string", "description": "Parameter description"}
},
"required": ["param"]
},
handler=your_async_handler
),
# ... existing tools
]Edit configs/cli_agent_config.json for CLI defaults, or
configs/frontend_agent_config.json for web-session defaults:
{
"model_name": "anthropic/claude-sonnet-4-5-20250929",
"mcpServers": {
"your-server-name": {
"transport": "http",
"url": "https://example.com/mcp",
"headers": {
"Authorization": "Bearer ${YOUR_TOKEN}"
}
}
}
}Note: Environment variables like ${YOUR_TOKEN} are auto-substituted from .env.