# 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: {json} 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 `{...}` 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`.