adaptive-model / README.md
joduor's picture
init: adaptive-model β€” handler, gateway, mcp-server, training
1de92bf verified
|
Raw
History Blame Contribute Delete
4.14 kB
# 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`.