<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Auto | Twinkle</title><link>https://modelscope.github.io/twinkle-web/docs/components/auto/</link><atom:link href="https://modelscope.github.io/twinkle-web/docs/components/auto/index.xml" rel="self" type="application/rss+xml"/><description>Auto</description><generator>HugoBlox Kit (https://hugoblox.com)</generator><language>en-us</language><image><url>https://modelscope.github.io/twinkle-web/media/logo_hu_fedc6a0bfe689b18.png</url><title>Auto</title><link>https://modelscope.github.io/twinkle-web/docs/components/auto/</link></image><item><title>Auto-Research</title><link>https://modelscope.github.io/twinkle-web/docs/components/auto/auto-research/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://modelscope.github.io/twinkle-web/docs/components/auto/auto-research/</guid><description>&lt;p&gt;Twinkle Auto is a terminal-based intelligent training assistant that lets you &lt;strong&gt;control, monitor, and debug ML training through natural language&lt;/strong&gt;. It combines a chat-driven AI agent with an automated health monitor that can detect and fix training failures autonomously.&lt;/p&gt;
&lt;h2 id="architecture-overview"&gt;Architecture Overview&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;┌──────────────────────────────────────────────────────────┐&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="n"&gt;TwinkleAuto&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;│&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;│&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="n"&gt;Components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;│&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="n"&gt;AgentLoop&lt;/span&gt; &lt;span class="err"&gt;───&lt;/span&gt; &lt;span class="n"&gt;LLM&lt;/span&gt; &lt;span class="k"&gt;tool&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;calling&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="err"&gt;│&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="n"&gt;TrainingMonitor&lt;/span&gt; &lt;span class="err"&gt;───&lt;/span&gt; &lt;span class="n"&gt;periodic&lt;/span&gt; &lt;span class="n"&gt;health&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;fix&lt;/span&gt; &lt;span class="err"&gt;│&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="n"&gt;LocalConnection&lt;/span&gt; &lt;span class="err"&gt;───&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt; &lt;span class="n"&gt;based&lt;/span&gt; &lt;span class="n"&gt;communication&lt;/span&gt; &lt;span class="err"&gt;│&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="n"&gt;SkillManager&lt;/span&gt; &lt;span class="err"&gt;───&lt;/span&gt; &lt;span class="n"&gt;async&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt; &lt;span class="n"&gt;loading&lt;/span&gt; &lt;span class="err"&gt;│&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;└──────────────────────────────────────────────────────────┘&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="installation--launch"&gt;Installation &amp;amp; Launch&lt;/h2&gt;
&lt;p&gt;Auto is part of the &lt;code&gt;twinkle-client&lt;/code&gt; package:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pip install twinkle-client
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="command-line-usage"&gt;Command-Line Usage&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Basic launch (uses default local Ollama endpoint)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;twinkle-auto
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Specify LLM backend&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;twinkle-auto --llm-base-url http://localhost:11434/v1 --llm-model qwen3.5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Attach to an existing training run&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;twinkle-auto --run-id my-grpo-run
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Use a remote API (e.g., OpenAI-compatible)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;twinkle-auto --llm-base-url https://api.example.com/v1 --llm-api-key sk-xxx --llm-model gpt-4o
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Enable debug logging&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;twinkle-auto --verbose
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or run as a Python module:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python -m twinkle_client.auto
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="cli-options"&gt;CLI Options&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Env Var&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--run-id&lt;/code&gt;, &lt;code&gt;-r&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TWINKLE_AUTO_RUN_ID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Attach to an existing training run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--llm-base-url&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TWINKLE_LLM_BASE_URL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;http://localhost:11434/v1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;LLM API base URL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--llm-model&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TWINKLE_LLM_MODEL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;qwen3.5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;LLM model name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--llm-api-key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TWINKLE_LLM_API_KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;not-needed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;LLM API key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--verbose&lt;/code&gt;, &lt;code&gt;-v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TWINKLE_AUTO_VERBOSE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enable DEBUG logging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--version&lt;/code&gt;, &lt;code&gt;-V&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Show version and exit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="chat-agent"&gt;Chat Agent&lt;/h2&gt;
&lt;p&gt;The core of Auto is an &lt;strong&gt;LLM-powered tool-calling agent&lt;/strong&gt; (&lt;code&gt;AgentLoop&lt;/code&gt;) that processes natural language commands through an OpenAI-compatible API. The agent maintains conversation history with automatic pruning (last 50 messages) and supports up to 10 tool-calling rounds per interaction.&lt;/p&gt;
&lt;h3 id="what-you-can-say"&gt;What You Can Say&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Training lifecycle:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;List my training runs&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;Start a new GRPO training with Qwen3.5-4B on gsm8k&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;Pause the current run&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;Resume training&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;Stop training&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Server management:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;Start the server with Qwen3.5-4B and a Qwen3.5-72B sampler on 2 GPUs&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;Shut down the server&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;How many GPUs are available?&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Monitoring &amp;amp; analysis:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;How is the training going?&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;Show me the reward-related metrics&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;Zoom into steps 100-200&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;Reset the chart view&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Search:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;Search for math datasets&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&amp;ldquo;Find Qwen models on ModelScope&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="available-tools"&gt;Available Tools&lt;/h3&gt;
&lt;p&gt;The agent has access to 13 built-in tools:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_training_runs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all training runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_training_status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get detailed status and recent metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;start_server&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start Ray cluster + Twinkle Server (idempotent)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;shutdown_server&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shut down server and release GPU resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;start_training&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create and launch a new training run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;select_run&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Switch monitoring to a different run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pause_training&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pause training (SIGKILL, server retains state)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;resume_training&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resume by re-launching the client script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stop_training&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop training (SIGTERM, saves checkpoint)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;update_script&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update training script with version archiving&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_supported_models&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Query server for available models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;search_datasets&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search ModelScope for datasets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;search_models&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search ModelScope for models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;zoom_metrics&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adjust metrics chart view range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;select_metrics&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Choose which metrics to display (max 4)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_cluster_info&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get GPU/cluster resource info&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="server-startup"&gt;Server Startup&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;start_server&lt;/code&gt; tool automates a multi-step pipeline:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;GPU detection&lt;/strong&gt; — &lt;code&gt;nvidia-smi&lt;/code&gt; hardware scan&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPU allocation&lt;/strong&gt; — partition GPUs between training model and samplers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Config generation&lt;/strong&gt; — auto-create &lt;code&gt;server_config.yaml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ray cluster startup&lt;/strong&gt; — multi-node GPU partitioning with isolated &lt;code&gt;CUDA_VISIBLE_DEVICES&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server launch&lt;/strong&gt; — start Twinkle Server as background process&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Health check&lt;/strong&gt; — poll &lt;code&gt;/api/v1/healthz&lt;/code&gt; until ready&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Multi-model topology is supported: 1 training model + N sampler/teacher models.&lt;/p&gt;
&lt;h3 id="skills-system"&gt;Skills System&lt;/h3&gt;
&lt;p&gt;Auto supports extensible skill plugins loaded from three sources:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Bundled skills&lt;/strong&gt; — shipped inside &lt;code&gt;twinkle_client/skills/bundled/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User-local skills&lt;/strong&gt; — &lt;code&gt;~/.cache/twinkle/auto/skills/local/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Community skills&lt;/strong&gt; — fetched from ModelScope (best-effort, 10s timeout)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Skills are loaded asynchronously after startup and injected into the agent&amp;rsquo;s system prompt. The agent is usable immediately even before skills finish loading.&lt;/p&gt;
&lt;h2 id="training-monitor-auto-fix"&gt;Training Monitor (Auto-Fix)&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;TrainingMonitor&lt;/code&gt; is a background service that runs every &lt;strong&gt;30 seconds&lt;/strong&gt;, collecting all available signals about the current training run and feeding them to the LLM for analysis.&lt;/p&gt;
&lt;h3 id="collected-signals"&gt;Collected Signals&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Process status&lt;/strong&gt;: alive / dead / unknown&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;output.log tail&lt;/strong&gt;: last 1500 chars (prioritizes tracebacks)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metrics&lt;/strong&gt;: recent entries + first-half vs second-half trend analysis&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stall duration&lt;/strong&gt;: seconds since last metric was produced&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Current train.py&lt;/strong&gt;: full script source (for accurate fixes)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="decision-framework"&gt;Decision Framework&lt;/h3&gt;
&lt;p&gt;The LLM classifies each check into one of three actions:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decision&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LGTM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Training progressing normally&lt;/td&gt;
&lt;td&gt;No action&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;WARNING&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Loss plateau, reward hacking, KL explosion, etc.&lt;/td&gt;
&lt;td&gt;Relay observation to user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FIX&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Script crashed, process dead with traceback&lt;/td&gt;
&lt;td&gt;Auto-fix and restart&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="auto-fix-pipeline"&gt;Auto-Fix Pipeline&lt;/h3&gt;
&lt;p&gt;When a FIX is needed:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;LLM outputs diagnosis + complete fixed script&lt;/li&gt;
&lt;li&gt;Monitor archives the old &lt;code&gt;train.py&lt;/code&gt; as &lt;code&gt;train_v{N}.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Writes the fixed script as the new &lt;code&gt;train.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Re-launches training via &lt;code&gt;resume_training&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Resets stall tracking for the new attempt&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Safety guardrails:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Max &lt;strong&gt;3 auto-fix attempts&lt;/strong&gt; per run (prevents infinite retry loops)&lt;/li&gt;
&lt;li&gt;Fix attempts are tracked per &lt;code&gt;run_id&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Snapshot deduplication avoids re-analyzing unchanged states&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="file-based-connection"&gt;File-Based Connection&lt;/h2&gt;
&lt;p&gt;Auto communicates with training processes through the local filesystem:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;~/.cache/twinkle/{run_id}/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── meta.json — run metadata (model_id, config, status, pid)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── metrics.jsonl — one JSON object per step (incremental)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── output.log — combined stdout+stderr from training
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── train.py — current active training script
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── train_v{N}.py — archived previous script versions
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="training-control-model"&gt;Training Control Model&lt;/h3&gt;
&lt;p&gt;In Server Mode, the Twinkle Server retains all model/optimizer state in GPU memory:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pause&lt;/strong&gt; = kill client process (SIGKILL) — server state preserved&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resume&lt;/strong&gt; = re-launch client script — seamlessly continues training&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stop&lt;/strong&gt; = SIGTERM — triggers checkpoint saving then exits&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shut down server&lt;/strong&gt; = releases GPU resources, &lt;strong&gt;destroys&lt;/strong&gt; model state&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="trainingruntime-script-integration"&gt;TrainingRuntime (Script Integration)&lt;/h2&gt;
&lt;p&gt;Training scripts use &lt;code&gt;TrainingRuntime&lt;/code&gt; to integrate with Auto:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twinkle_client.auto.runtime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TrainingRuntime&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TrainingRuntime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my-grpo-run&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Qwen/Qwen3.5-4B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lr&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1e-5&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register_graceful_shutdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataloader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataloader&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# ... training logic ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log_metrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reward&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;reward&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;grad_norm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;gn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Completed step &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;, loss=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;.4f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="key-methods"&gt;Key Methods&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;start(model_id, config, script_path)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Initialize run directory and metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;log_metrics(**kwargs)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write metrics entry to &lt;code&gt;metrics.jsonl&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;log(message)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print log message (captured as &lt;code&gt;output.log&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_resume_info()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get &lt;code&gt;last_step&lt;/code&gt; for resuming from checkpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;finish(status)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mark training as finished, close files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;register_graceful_shutdown(model, dataloader)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Register SIGTERM handler that saves checkpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="resume-support"&gt;Resume Support&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;TrainingRuntime&lt;/code&gt; automatically saves training progress to &lt;code&gt;meta.json&lt;/code&gt; (throttled to every 5 seconds). Scripts can use &lt;code&gt;get_resume_info()&lt;/code&gt; to resume from the last saved step:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TrainingRuntime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my-run&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;resume&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_resume_info&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;global_step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;last_step&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;global_step&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dataloader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;skip_consumed_samples&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;global_step&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Resuming from step &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;global_step&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="graceful-shutdown"&gt;Graceful Shutdown&lt;/h3&gt;
&lt;p&gt;When &lt;code&gt;register_graceful_shutdown()&lt;/code&gt; is called, a SIGTERM handler is installed that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Saves model checkpoint (LoRA weights + optimizer state)&lt;/li&gt;
&lt;li&gt;Saves dataloader position (&lt;code&gt;consumed_train_samples&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Logs the checkpoint path&lt;/li&gt;
&lt;li&gt;Marks training as &lt;code&gt;stopped&lt;/code&gt; and exits&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="logging"&gt;Logging&lt;/h2&gt;
&lt;p&gt;All logs are written to &lt;code&gt;./auto.log&lt;/code&gt; (current working directory):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rotated at 5MB with 3 backups&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No console output&lt;/strong&gt; — all output goes to the log file&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;--verbose&lt;/code&gt; for DEBUG level logging&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>SkillProvider</title><link>https://modelscope.github.io/twinkle-web/docs/components/auto/skillprovider/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://modelscope.github.io/twinkle-web/docs/components/auto/skillprovider/</guid><description>&lt;p&gt;The skill system allows Twinkle Auto&amp;rsquo;s agent to dynamically load specialized knowledge from external sources (Git repos, APIs, local files) and inject them into the LLM&amp;rsquo;s system prompt.&lt;/p&gt;
&lt;h2 id="architecture"&gt;Architecture&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Skill&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dataclass holding a single skill&amp;rsquo;s name, content, and source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SkillProvider&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Abstract base class for fetching skills from a source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SkillManager&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Orchestrates multiple providers, aggregates skills for prompt injection&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="skill-dataclass"&gt;Skill Dataclass&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@dataclasses.dataclass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Skill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="c1"&gt;# Short identifier (typically filename without extension)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="c1"&gt;# Full markdown content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="c1"&gt;# Provider name + relative path for traceability&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="creating-a-custom-provider"&gt;Creating a Custom Provider&lt;/h2&gt;
&lt;p&gt;Subclass &lt;code&gt;SkillProvider&lt;/code&gt; and implement &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;fetch()&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twinkle_client.skills.base&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SkillProvider&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MySkillProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SkillProvider&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@property&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;my-skills&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Download/clone skill files to self.cache_dir&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# e.g., git clone, API download, file copy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The default &lt;code&gt;load_skills()&lt;/code&gt; scans &lt;code&gt;self.cache_dir&lt;/code&gt; for &lt;code&gt;.md&lt;/code&gt; files (skipping README, LICENSE, etc.) and returns &lt;code&gt;Skill&lt;/code&gt; objects.&lt;/p&gt;
&lt;h2 id="skillmanager"&gt;SkillManager&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twinkle_client.skills.manager&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SkillManager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;manager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SkillManager&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_provider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;another_provider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Fetch and load all skills&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;skills&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Format for LLM system prompt injection&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;prompt_section&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format_for_prompt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="key-methods"&gt;Key Methods&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;register(provider)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add a skill provider&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;load_all()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fetch + load from all providers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;format_for_prompt()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Render skills as formatted text for system prompt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_skill_names()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List names of loaded skills&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="cache-directory"&gt;Cache Directory&lt;/h2&gt;
&lt;p&gt;By default, skills are cached at &lt;code&gt;~/.cache/twinkle/auto/skills/&amp;lt;provider_name&amp;gt;/&lt;/code&gt;. Override by passing &lt;code&gt;cache_dir&lt;/code&gt; to the provider constructor.&lt;/p&gt;</description></item></channel></rss>