new wireviz generate from description via openai
This commit is contained in:
parent
0a9b8fb72f
commit
e66cfe97de
217
README.md
217
README.md
@ -3,210 +3,105 @@
|
|||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[](https://pypi.org/project/mcp-arduino-server/)
|
[](https://pypi.org/project/mcp-arduino-server/)
|
||||||
|
|
||||||
MCP Server for Arduino CLI providing sketch, board, library, and file management tools. Powered by FastMCP.
|
A FastMCP-powered bridge exposing `arduino-cli` functionality via the Model Context Protocol (MCP). Manage sketches, boards, libraries, files, plus generate WireViz schematics from YAML or natural language.
|
||||||
|
|
||||||
## Quick Start
|
## Requirements
|
||||||
|
|
||||||
Install from PyPI and run:
|
- **Python ≥3.10**
|
||||||
|
- **arduino-cli** in `PATH`
|
||||||
```bash
|
- **MCP SDK** (`mcp[cli]`)
|
||||||
pip install mcp-arduino-server
|
- **WireViz** (optional; for diagram generation)
|
||||||
mcp-arduino-server
|
- **OPENAI_API_KEY** (for AI‑powered WireViz)
|
||||||
```
|
- **thefuzz[speedup]** (optional; enables fuzzy local library search)
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This server acts as a bridge between the Model Context Protocol (MCP) and the `arduino-cli`, allowing AI agents or other MCP clients to interact with Arduino development workflows. It provides tools for managing sketches, compiling code, uploading to boards, managing libraries, discovering hardware, and performing basic file operations within a restricted environment.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
* **Sketch Management**: Create, list, read, and write Arduino sketches (`.ino`, `.h` files).
|
|
||||||
* Writing the main `.ino` file automatically triggers compilation for validation.
|
|
||||||
* **Auto-Open**: Newly created `.ino` files are automatically opened in your default editor.
|
|
||||||
* **WireViz Circuit Diagrams**:
|
|
||||||
* **YAML Authoring Help:** Use the built-in `getWirevizInstructions` tool to fetch comprehensive guidelines and an example for creating valid WireViz YAML. The instructions cover how to define connectors (components), cables, connections, and required metadata, along with a checklist and color code reference.
|
|
||||||
* **Diagram Generation:** Use the `generate_diagram_from_yaml` tool to generate a circuit diagram PNG from your YAML. You can specify a sketch directory for output, or let the tool create a timestamped folder. The PNG is returned as base64 and opened automatically in your image viewer.
|
|
||||||
* **Typical Workflow:**
|
|
||||||
1. Call `getWirevizInstructions` to see the YAML structure and example.
|
|
||||||
2. Author your YAML to describe your Arduino circuit.
|
|
||||||
3. Call `generate_diagram_from_yaml` with your YAML to get a ready-to-use PNG wiring diagram.
|
|
||||||
* **Error Handling:** The server validates YAML structure, manages output files, and provides clear error messages for invalid YAML, missing dependencies, or WireViz failures.
|
|
||||||
* **Example YAML:** (see the output of `getWirevizInstructions` for a full template)
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
connectors:
|
|
||||||
Arduino Uno:
|
|
||||||
pinlabels: ["5V", "GND", "D2", "D3", "A4", "A5"]
|
|
||||||
notes: Main control board
|
|
||||||
SSD1306 OLED Display:
|
|
||||||
pinlabels: ["VCC", "GND", "SCL", "SDA"]
|
|
||||||
notes: Display module
|
|
||||||
# ... more components ...
|
|
||||||
cables:
|
|
||||||
W_SSD1306_OLED:
|
|
||||||
colors: [RD, BK, TQ, VT]
|
|
||||||
category: bundle
|
|
||||||
connections:
|
|
||||||
- # Example connection
|
|
||||||
- Arduino Uno: [3]
|
|
||||||
- W_SSD1306_OLED: [1]
|
|
||||||
- SSD1306 OLED Display: [1]
|
|
||||||
metadata:
|
|
||||||
description: "Wiring diagram for Arduino Uno with SSD1306 OLED Display and Push Buttons"
|
|
||||||
author: "User"
|
|
||||||
date: "2024-06-23"
|
|
||||||
```
|
|
||||||
|
|
||||||
* **Code Verification**: Compile sketches using `verify_code` without uploading.
|
|
||||||
* **Uploading**: Compile and upload sketches to connected boards.
|
|
||||||
* **Library Management**:
|
|
||||||
* Search the online Arduino Library Manager index.
|
|
||||||
* Search local platform libraries (fuzzy search if `thefuzz` is installed).
|
|
||||||
* Install libraries from the index.
|
|
||||||
* List examples from installed libraries.
|
|
||||||
* **Board Management**:
|
|
||||||
* Discover connected boards and their details (Port, Name, FQBN).
|
|
||||||
* List platform libraries associated with connected boards.
|
|
||||||
* Search the online board index for FQBNs.
|
|
||||||
* **File Operations**: Basic, restricted file reading, writing, renaming, and removal within the user's home directory or designated sketch directories.
|
|
||||||
* **Security**: Operations are sandboxed primarily to `~/Documents/Arduino_MCP_Sketches/` and the user's home directory (`~`) with strong warnings for destructive actions.
|
|
||||||
* **Robust Error Handling & Logging**: Extensive logging, improved error messages, and strict path validation for all file operations. Security is emphasized throughout.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
* **Python**: **3.8+** (3.10+ recommended; required by dependencies like `mcp[cli]`)
|
|
||||||
* **arduino-cli**: Must be installed and accessible in the system `PATH` or common locations (e.g., `/usr/local/bin`, `/opt/homebrew/bin`). The server attempts auto-detection.
|
|
||||||
* **WireViz**: Required for circuit diagram generation. Install and ensure it's in your PATH.
|
|
||||||
* **MCP SDK**: Installed via the project dependencies (`mcp[cli]`).
|
|
||||||
* **Fuzzy Search (Optional but Recommended)**: Installed via project dependencies (`thefuzz[speedup]>=0.20.0`). Enables fuzzy matching for local library search.
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Install via pip (recommended for most users):
|
**From PyPI**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install mcp-arduino-server
|
pip install mcp-arduino-server
|
||||||
```
|
```
|
||||||
|
|
||||||
For development or advanced usage, clone the repository and install dependencies:
|
**From source**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/Volt23/mcp-arduino-server.git
|
git clone https://github.com/Volt23/mcp-arduino-server.git
|
||||||
cd mcp-arduino-server
|
cd mcp-arduino-server
|
||||||
pip install .
|
pip install .
|
||||||
```
|
```
|
||||||
|
|
||||||
**Set up Python 3.10+**: Ensure you have Python 3.10 or newer. Using `pyenv` is recommended:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pyenv install 3.11.6 # Or latest 3.10+
|
|
||||||
pyenv local 3.11.6
|
|
||||||
```
|
|
||||||
|
|
||||||
**Ensure `arduino-cli` is installed and configured:**
|
|
||||||
- Follow the official [arduino-cli installation guide](https://arduino.github.io/arduino-cli/latest/installation/).
|
|
||||||
- You may need to install board cores (e.g., `arduino-cli core install arduino:avr`).
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
The server uses the following default paths and settings. Some can be overridden via environment variables.
|
Environment variables override defaults:
|
||||||
|
|
||||||
* **Sketches Base Directory**: `~/Documents/Arduino_MCP_Sketches/`
|
| Variable | Default / Description |
|
||||||
* **Build Temp Directory**: `~/Documents/Arduino_MCP_Sketches/_build_temp/`
|
|----------------------|-----------------------------------------------------|
|
||||||
* **Arduino Data Directory**: Auto-detected (`~/.arduino15` or `~/Library/Arduino15`)
|
| ARDUINO_CLI_PATH | auto-detected |
|
||||||
* **Arduino User Directory**: `~/Documents/Arduino/`
|
| WIREVIZ_PATH | auto-detected |
|
||||||
* **Arduino CLI Path**: Auto-detected via `shutil.which` and common paths. Override with `ARDUINO_CLI_PATH` environment variable.
|
| MCP_SKETCH_DIR | `~/Documents/Arduino_MCP_Sketches/` |
|
||||||
* **WireViz Path**: Auto-detected via `shutil.which` (expects `wireviz` command). Override with `WIREVIZ_PATH` environment variable.
|
| LOG_LEVEL | `INFO` |
|
||||||
* **Default FQBN (for auto-compile on write)**: `arduino:avr:uno`. Override via the `board_fqbn` argument in `write_file`.
|
| OPENAI_API_KEY | your OpenAI API key (required for AI‑powered WireViz)|
|
||||||
* **Log Level**: Controlled by the `LOG_LEVEL` environment variable (e.g., `DEBUG`, `INFO`, `WARNING`). Defaults to `INFO`.
|
| OPENROUTER_API_KEY | optional alternative to `OPENAI_API_KEY` |
|
||||||
|
|
||||||
## Usage
|
## Quick Start
|
||||||
|
|
||||||
Run the server using the installed command-line script within its environment:
|
```bash
|
||||||
|
mcp-arduino-server
|
||||||
|
```
|
||||||
|
|
||||||
* **Using `uv`:**
|
Server listens on STDIO for JSON-RPC MCP calls. Key methods:
|
||||||
```bash
|
|
||||||
uv run mcp-arduino-server
|
|
||||||
```
|
|
||||||
* **Using `pip` (with activated venv):**
|
|
||||||
```bash
|
|
||||||
# Ensure your virtual environment is activated (source .venv/bin/activate)
|
|
||||||
mcp-arduino-server
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using WireViz Tools
|
### Sketches
|
||||||
|
- `create_new_sketch(name)`
|
||||||
|
- `list_sketches()`
|
||||||
|
- `read_file(path)`
|
||||||
|
- `write_file(path, content[, board_fqbn])` _(auto-compiles & opens `.ino`)_
|
||||||
|
|
||||||
- **YAML Authoring Help:** Call `getWirevizInstructions()` to receive comprehensive guidelines, a checklist, and a ready-to-use example for authoring valid WireViz YAML for Arduino diagrams.
|
### Build & Deploy
|
||||||
- **Diagram Generation:** Call `generate_diagram_from_yaml(yaml_content: str, sketch_name: str = "", output_filename_base: str = "circuit")` to generate a PNG wiring diagram from your YAML. The tool validates your YAML, manages output files, returns a confirmation and the PNG image (base64), and opens it automatically in your image viewer.
|
- `verify_code(sketch, board_fqbn)`
|
||||||
- **Workflow:**
|
- `upload_sketch(sketch, port, board_fqbn)`
|
||||||
1. Use `getWirevizInstructions` to learn the YAML format.
|
|
||||||
2. Write your YAML describing your circuit.
|
|
||||||
3. Use `generate_diagram_from_yaml` to create and view your diagram.
|
|
||||||
|
|
||||||
### Auto-Open Feature
|
### Libraries
|
||||||
|
- `lib_search(name[, limit])`
|
||||||
|
- `lib_install(name)`
|
||||||
|
- `list_library_examples(name)`
|
||||||
|
|
||||||
- When you create a new sketch or generate a diagram, the relevant file will open automatically in your system's default application (editor or image viewer).
|
### Boards
|
||||||
|
- `list_boards()`
|
||||||
|
- `board_search(query)`
|
||||||
|
|
||||||
### Error Handling
|
### File Ops
|
||||||
|
- `rename_file(src, dest)`
|
||||||
|
- `remove_file(path)` _(destructive; operations sandboxed to home & sketch directories)_
|
||||||
|
|
||||||
- All file operations and CLI interactions include robust error messages and logging. Check logs for troubleshooting details.
|
### WireViz Diagrams
|
||||||
|
- `generate_circuit_diagram_from_description(desc, sketch="", output_base="circuit")` _(AI‑powered; requires `OPENAI_API_KEY`, opens PNG automatically)_
|
||||||
|
|
||||||
The server will start and listen for connections from an MCP client via standard input/output (`stdio`).
|
## MCP Client Configuration
|
||||||
|
|
||||||
### Integrating with MCP Clients (e.g., Claude Desktop)
|
To integrate with MCP clients (e.g., Claude Desktop), set your OpenAI API key in the environment (or alternatively `OPENROUTER_API_KEY` for OpenRouter):
|
||||||
|
|
||||||
When configuring an MCP client (such as Claude Desktop) to launch this server, you must ensure the `command` in your JSON config points to the correct executable. **If you are using pyenv or a custom Python environment, specify the full path to the `mcp-arduino-server` binary where it was installed.**
|
|
||||||
|
|
||||||
For example, to ensure WireViz is always found regardless of PATH issues, you can set the `WIREVIZ_PATH` environment variable in your MCP client config if supported:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"arduino": {
|
"arduino": {
|
||||||
"command": "/Users/<your-username>/.pyenv/versions/<your-python-version>/bin/mcp-arduino-server",
|
"command": "/path/to/mcp-arduino-server",
|
||||||
"args": [],
|
"args": [],
|
||||||
"env": {
|
"env": {
|
||||||
"WIREVIZ_PATH": "/Users/<your-username>/.pyenv/versions/<your-python-version>/bin/wireviz"
|
"WIREVIZ_PATH": "/path/to/wireviz",
|
||||||
|
"OPENAI_API_KEY": "<your-openai-api-key>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Replace `<your-username>` and `<your-python-version>` with your actual username and Python version. This ensures the MCP client launches the correct environment and always finds WireViz, even if your PATH is not set up for GUI apps.
|
## Troubleshooting
|
||||||
|
|
||||||
Replace `<your-username>` and `<your-python-version>` with your actual username and Python version as appropriate. This ensures the MCP client launches the correct environment and finds all dependencies.
|
- Set `LOG_LEVEL=DEBUG` for verbose logs.
|
||||||
|
- Verify file and serial-port permissions.
|
||||||
## Available Tools (MCP Interface)
|
- Install missing cores: `arduino-cli core install <spec>`.
|
||||||
|
- Run `arduino-cli` commands manually to debug.
|
||||||
The following tools are exposed via the MCP interface:
|
|
||||||
|
|
||||||
* `create_new_sketch(sketch_name: str)`: Creates a new sketch directory and `.ino` file.
|
|
||||||
* `list_sketches()`: Lists valid sketches in the sketches directory.
|
|
||||||
* `read_file(filepath: str)`: Reads a file; concatenates all `.ino`/`.h` files if reading the main sketch `.ino`.
|
|
||||||
* `write_file(filepath: str, content: str, board_fqbn: str = DEFAULT_FQBN)`: Writes content to a file; restricted paths; auto-compiles main `.ino` files.
|
|
||||||
* `rename_file(old_path: str, new_path: str)`: Renames/moves a file/directory within the home directory.
|
|
||||||
* `remove_file(filepath: str)`: Removes a file (not directories) within the home directory. **Irreversible.**
|
|
||||||
* `list_boards()`: Lists connected boards, their FQBNs, and platform libraries.
|
|
||||||
* `board_search(board_name_query: str)`: Searches the online index for board FQBNs.
|
|
||||||
* `verify_code(sketch_name: str, board_fqbn: str)`: Compiles a sketch without uploading.
|
|
||||||
* `upload_sketch(sketch_name: str, port: str, board_fqbn: str)`: Compiles and uploads a sketch.
|
|
||||||
* `lib_search(library_name: str, limit: int = 15)`: Searches online and local platform libraries.
|
|
||||||
* `lib_install(library_name: str)`: Installs/updates a library from the index.
|
|
||||||
* `list_library_examples(library_name: str)`: Lists examples for an installed library.
|
|
||||||
* `getWirevizInstructions()`: Returns detailed YAML authoring instructions and a template for WireViz diagrams.
|
|
||||||
* `generate_diagram_from_yaml(yaml_content: str, sketch_name: str = "", output_filename_base: str = "circuit")`: Generates a PNG wiring diagram from YAML, returns image and confirmation, opens PNG automatically.
|
|
||||||
|
|
||||||
Refer to the server script's docstrings (`src/mcp_arduino_server/server.py`) for detailed arguments, return values, and potential errors for each tool.
|
|
||||||
|
|
||||||
## Debugging Tips
|
|
||||||
|
|
||||||
* **Check Server Logs**: Detailed errors from `arduino-cli` are logged by the server. Increase verbosity with `export LOG_LEVEL=DEBUG`.
|
|
||||||
* **Permissions**: Ensure the user running the server has write access to sketch/build directories and read/write access to serial ports (e.g., add user to `dialout` group on Linux).
|
|
||||||
* **Environment PATH**: Verify `arduino-cli` and necessary toolchains (e.g., `avr-gcc`, `bossac`) are in the `PATH` accessible to the server process.
|
|
||||||
* **Cores/Toolchains**: Use `arduino-cli core install <core_spec>` (e.g., `arduino:avr`) if compilation fails due to missing cores.
|
|
||||||
* **`arduino-cli` Commands**: Test `arduino-cli` commands directly in your terminal to isolate issues.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the MIT License.
|
MIT
|
||||||
|
|
||||||
|
@ -78,6 +78,18 @@ import re
|
|||||||
import shutil # Used for finding executable and file operations
|
import shutil # Used for finding executable and file operations
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys # Added for exit calls and platform detection
|
import sys # Added for exit calls and platform detection
|
||||||
|
import openai # For GPT-4.1 API calls
|
||||||
|
|
||||||
|
# --- OpenAI API Key Management ---
|
||||||
|
_OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", None)
|
||||||
|
|
||||||
|
def set_openai_api_key(key: str):
|
||||||
|
global _OPENAI_API_KEY
|
||||||
|
_OPENAI_API_KEY = key
|
||||||
|
os.environ["OPENAI_API_KEY"] = key
|
||||||
|
log.info("OpenAI API key set.")
|
||||||
|
return "OpenAI API key set."
|
||||||
|
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import (Any, AsyncIterator, Dict, List, Optional, Set, Tuple,
|
from typing import (Any, AsyncIterator, Dict, List, Optional, Set, Tuple,
|
||||||
@ -1823,7 +1835,7 @@ async def write_file(filepath: str, content: str, board_fqbn: str = DEFAULT_FQBN
|
|||||||
raise Exception(error_msg) from e
|
raise Exception(error_msg) from e
|
||||||
|
|
||||||
|
|
||||||
@mcp.tool()
|
# @mcp.tool()
|
||||||
async def rename_file(old_path: str, new_path: str) -> str:
|
async def rename_file(old_path: str, new_path: str) -> str:
|
||||||
"""
|
"""
|
||||||
Renames or moves a file or directory.
|
Renames or moves a file or directory.
|
||||||
@ -1889,7 +1901,7 @@ async def rename_file(old_path: str, new_path: str) -> str:
|
|||||||
raise Exception(error_msg) from e
|
raise Exception(error_msg) from e
|
||||||
|
|
||||||
|
|
||||||
@mcp.tool()
|
# @mcp.tool()
|
||||||
async def remove_file(filepath: str) -> str:
|
async def remove_file(filepath: str) -> str:
|
||||||
"""
|
"""
|
||||||
Removes (deletes) a specified file.
|
Removes (deletes) a specified file.
|
||||||
@ -1948,7 +1960,107 @@ async def remove_file(filepath: str) -> str:
|
|||||||
# @mcp.resource("wireviz://instructions")
|
# @mcp.resource("wireviz://instructions")
|
||||||
# async def get_wireviz_instructions_resource() -> str:
|
# async def get_wireviz_instructions_resource() -> str:
|
||||||
|
|
||||||
|
async def set_openai_api_key_tool(api_key: str) -> str:
|
||||||
|
"""
|
||||||
|
Sets the OpenAI API key for GPT-4.1 calls at runtime.
|
||||||
|
"""
|
||||||
|
return set_openai_api_key(api_key)
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
|
async def generate_circuit_diagram_from_description(
|
||||||
|
description: str,
|
||||||
|
sketch_name: str = "",
|
||||||
|
output_filename_base: str = "circuit"
|
||||||
|
) -> List[Union[types.TextContent, types.ImageContent]]:
|
||||||
|
"""
|
||||||
|
Generates a circuit diagram PNG from a natural language description of components and connections.
|
||||||
|
Uses OpenAI GPT-4.1 to convert the description and the WireViz guide into valid YAML, then generates the PNG.
|
||||||
|
Returns both the generated YAML and the PNG image.
|
||||||
|
"""
|
||||||
|
if not description or not description.strip():
|
||||||
|
raise ValueError("Description cannot be empty.")
|
||||||
|
# Get OpenAI/OpenRouter API key (robust lookup)
|
||||||
|
api_key = _OPENAI_API_KEY or os.environ.get("OPENAI_API_KEY") or os.environ.get("OPENROUTER_API_KEY")
|
||||||
|
if not api_key:
|
||||||
|
raise ValueError("OpenAI/OpenRouter API key is not set. Use set_openai_api_key_tool() or set the OPENAI_API_KEY or OPENROUTER_API_KEY environment variable.")
|
||||||
|
|
||||||
|
# Get the WireViz guide
|
||||||
|
wireviz_guide = await getWirevizInstructions()
|
||||||
|
prompt = (
|
||||||
|
"You are a WireViz YAML expert. Convert the following user description into a valid WireViz YAML file "
|
||||||
|
"suitable for generating a circuit diagram. Follow the provided guidelines and examples. "
|
||||||
|
"Return ONLY the YAML, and enclose it between triple backticks as a YAML code block, like this: ```yaml ... ``` (do not add any explanation or text outside the code block).\n\n" +
|
||||||
|
"WireViz YAML Guidelines:\n" + wireviz_guide + "\n\n" +
|
||||||
|
"User Description:\n" + description.strip()
|
||||||
|
)
|
||||||
|
# --- Provider detection and setup ---
|
||||||
|
from openai import OpenAI as OpenAIClient
|
||||||
|
import re
|
||||||
|
# Heuristic: OpenRouter keys usually start with 'sk-or-' or 'sk-proj-'; OpenAI with 'sk-...'
|
||||||
|
is_openrouter = api_key.startswith("sk-or-") or api_key.startswith("sk-proj-") or os.environ.get("OPENAI_PROVIDER", "").lower() == "openrouter"
|
||||||
|
if is_openrouter:
|
||||||
|
base_url = "https://openrouter.ai/api/v1"
|
||||||
|
model = "openai/gpt-4.1-2025-04-14"
|
||||||
|
# Optionally allow headers from env/config
|
||||||
|
extra_headers = {}
|
||||||
|
referer = os.environ.get("OPENROUTER_REFERER")
|
||||||
|
xtitle = os.environ.get("OPENROUTER_XTITLE")
|
||||||
|
if referer:
|
||||||
|
extra_headers["HTTP-Referer"] = referer
|
||||||
|
if xtitle:
|
||||||
|
extra_headers["X-Title"] = xtitle
|
||||||
|
else:
|
||||||
|
base_url = None
|
||||||
|
model = "gpt-4.1-2025-04-14"
|
||||||
|
extra_headers = None
|
||||||
|
client = OpenAIClient(api_key=api_key, base_url=base_url) if base_url else OpenAIClient(api_key=api_key)
|
||||||
|
# --- End Provider setup ---
|
||||||
|
try:
|
||||||
|
def do_completion():
|
||||||
|
kwargs = dict(
|
||||||
|
model=model,
|
||||||
|
messages=[{"role": "user", "content": prompt}],
|
||||||
|
temperature=0.2,
|
||||||
|
max_tokens=1800,
|
||||||
|
)
|
||||||
|
if extra_headers:
|
||||||
|
kwargs["extra_headers"] = extra_headers
|
||||||
|
return client.chat.completions.create(**kwargs)
|
||||||
|
response = await asyncio.get_event_loop().run_in_executor(
|
||||||
|
None,
|
||||||
|
do_completion
|
||||||
|
)
|
||||||
|
llm_output = response.choices[0].message.content.strip()
|
||||||
|
# --- Robust YAML code block extraction ---
|
||||||
|
import re
|
||||||
|
code_block_pattern = re.compile(r"```(?:yaml)?\s*([\s\S]+?)\s*```", re.IGNORECASE)
|
||||||
|
match = code_block_pattern.search(llm_output)
|
||||||
|
if match:
|
||||||
|
yaml_content = match.group(1).strip()
|
||||||
|
else:
|
||||||
|
log.warning("No YAML code block found in LLM response. Using full response as YAML.")
|
||||||
|
yaml_content = llm_output
|
||||||
|
except Exception as e:
|
||||||
|
log.error(f"OpenAI GPT-4.1 call failed: {e}")
|
||||||
|
raise Exception(f"OpenAI GPT-4.1 call failed: {e}")
|
||||||
|
# Generate diagram from YAML
|
||||||
|
try:
|
||||||
|
result = await generate_diagram_from_yaml(
|
||||||
|
yaml_content=yaml_content,
|
||||||
|
sketch_name=sketch_name,
|
||||||
|
output_filename_base=output_filename_base
|
||||||
|
)
|
||||||
|
# Prepend the YAML content as a TextContent result
|
||||||
|
if isinstance(result, list):
|
||||||
|
yaml_msg = types.TextContent(type="text", text=f"Generated WireViz YAML:\n\n{yaml_content}")
|
||||||
|
return [yaml_msg] + result
|
||||||
|
else:
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
log.error(f"Failed to generate diagram from YAML: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
# @mcp.resource("wireviz://instructions")
|
||||||
async def getWirevizInstructions() -> str:
|
async def getWirevizInstructions() -> str:
|
||||||
"""
|
"""
|
||||||
Provides basic instructions and links on how to use WireViz YAML syntax
|
Provides basic instructions and links on how to use WireViz YAML syntax
|
||||||
@ -2149,7 +2261,6 @@ Checklist
|
|||||||
"""
|
"""
|
||||||
return instructions.strip()
|
return instructions.strip()
|
||||||
|
|
||||||
@mcp.tool()
|
|
||||||
async def generate_diagram_from_yaml(
|
async def generate_diagram_from_yaml(
|
||||||
yaml_content: str,
|
yaml_content: str,
|
||||||
sketch_name: str = "", # Changed: now a string with a default of empty string
|
sketch_name: str = "", # Changed: now a string with a default of empty string
|
||||||
|
Loading…
x
Reference in New Issue
Block a user