| # adaptive-model |
|
|
| Multi-adapter language model with two chat surfaces: |
|
|
| | Surface | Mechanism | UI | |
| |---------|-----------|-----| |
| | **Path A** β Custom GPT Action | OpenAPI schema β `/generate` | Markdown fallback | |
| | **Path B** β MCP Apps | MCP tool β `structuredContent` | Real interactive iframe widget | |
|
|
| Both surfaces share the same Hugging Face model backend and heuristic adapter router. |
|
|
| --- |
|
|
| ## Architecture |
|
|
| ``` |
| Chat turn |
| β |
| βΌ |
| lib/router.py ββ heuristic or explicit mode |
| β |
| βΌ |
| HF Inference Endpoint (handler.py) |
| β base model + 3 LoRA adapters (support / analytics / form) |
| β emits: <ui>{json}</ui> then prose |
| β |
| ββββ Path A ββ gateway/app.py (FastAPI) |
| β renders ui_spec β markdown |
| β served via openapi-schema.yaml β Custom GPT Action |
| β |
| ββββ Path B ββ mcp-server/server.py (FastMCP) |
| returns structuredContent |
| widget/adaptive.html renders in iframe |
| ``` |
|
|
| --- |
|
|
| ## Quick start |
|
|
| ```bash |
| cp .env.example .env |
| # fill in HF_ENDPOINT_URL, HF_TOKEN, adapter repo IDs |
| ``` |
|
|
| ### Path A β Custom GPT gateway |
|
|
| ```bash |
| pip install -r gateway/requirements.txt |
| python -m gateway.app # runs on :8000 |
| ``` |
|
|
| Paste `gateway/openapi-schema.yaml` into your GPT's Actions editor. |
| Set the server URL to your deployed gateway (ngrok / Railway / Fly.io). |
|
|
| ### Path B β MCP server |
|
|
| ```bash |
| pip install -r mcp-server/requirements.txt |
| |
| # stdio (local MCP client, e.g. Claude Desktop) |
| python mcp-server/server.py |
| |
| # SSE (remote clients, e.g. ChatGPT plugin host) |
| python mcp-server/server.py --http # listens on :3100 |
| ``` |
|
|
| Add to your MCP client config: |
| ```json |
| { |
| "mcpServers": { |
| "adaptive-model": { |
| "command": "python", |
| "args": ["mcp-server/server.py"] |
| } |
| } |
| } |
| ``` |
|
|
| --- |
|
|
| ## Hugging Face endpoint |
|
|
| 1. Push `hf-endpoint/handler.py` to your model repo on the Hub. |
| 2. Create an **Inference Endpoint** pointing at that repo. |
| - Hardware: minimum A10G (24 GB) for a 7B base + 3 LoRA adapters in bf16. |
| - Set env vars: `BASE_MODEL`, `ADAPTER_SUPPORT`, `ADAPTER_ANALYTICS`, `ADAPTER_FORM`. |
| 3. Copy the endpoint URL into `.env` as `HF_ENDPOINT_URL`. |
|
|
| --- |
|
|
| ## Adapter training |
|
|
| Adapters must be fine-tuned to emit `<ui>{...}</ui>` followed by prose. |
| Generate synthetic seed data to bootstrap each adapter: |
|
|
| ```bash |
| pip install -r hf-endpoint/requirements.txt |
| |
| python training/generate_examples.py --all --n 200 -o data/ |
| # writes data/support.jsonl data/analytics.jsonl data/form.jsonl |
| ``` |
|
|
| Then fine-tune with your preferred PEFT trainer (TRL `SFTTrainer` works well): |
|
|
| ```python |
| from trl import SFTTrainer, SFTConfig |
| from peft import LoraConfig |
| |
| lora_cfg = LoraConfig(r=16, lora_alpha=32, target_modules=["q_proj","v_proj"]) |
| trainer = SFTTrainer( |
| model=base_model, |
| args=SFTConfig(output_dir="./adapter-support", num_train_epochs=3), |
| train_dataset=support_dataset, |
| peft_config=lora_cfg, |
| ) |
| trainer.train() |
| trainer.push_to_hub("your-org/adapter-support") |
| ``` |
|
|
| --- |
|
|
| ## ui_spec contract |
| |
| The model emits one of these component shapes: |
| |
| ```jsonc |
| // form |
| {"component":"form","props":{"title":"...","fields":[{"name":"x","label":"X","type":"text","required":true}],"submitLabel":"Send"}} |
| |
| // chart |
| {"component":"chart","props":{"title":"...","type":"bar","data":{"labels":["Jan","Feb"],"datasets":[{"label":"Revenue","data":[400,600]}]}}} |
| |
| // card |
| {"component":"card","props":{"title":"...","body":"...","items":[{"label":"Status","value":"OK"}]}} |
| |
| // table |
| {"component":"table","props":{"columns":["Name","Value"],"rows":[["Alpha",1],["Beta",2]]}} |
| ``` |
| |
| The widget renders any of these interactively. |
| Path A degrades each to markdown via `lib/markdown_renderer.py`. |
|
|
| --- |
|
|
| ## Routing |
|
|
| The heuristic router (`lib/router.py`) scores keywords in the last user turn: |
|
|
| | Adapter | Triggers | |
| |---------|---------| |
| | `support` | error, help, issue, bug, broken, fix β¦ | |
| | `analytics` | chart, graph, trend, metric, dashboard β¦ | |
| | `form` | form, fill, submit, register, sign up β¦ | |
|
|
| Pass `mode` explicitly to override. Default is `support`. |
|
|