Add initial working version
This commit is contained in:
parent
e8612f5eed
commit
c69e6d390d
10
.gitignore
vendored
10
.gitignore
vendored
|
@ -0,0 +1,10 @@
|
|||
# Python-generated files
|
||||
__pycache__/
|
||||
*.py[oc]
|
||||
build/
|
||||
dist/
|
||||
wheels/
|
||||
*.egg-info
|
||||
|
||||
# Virtual environments
|
||||
.venv
|
1
.python-version
Normal file
1
.python-version
Normal file
|
@ -0,0 +1 @@
|
|||
3.13
|
55
README.md
Normal file
55
README.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
# TheVoices
|
||||
|
||||
A FastMCP server providing role-based AI assistant tools for specialized AI personas and LLM model discovery.
|
||||
|
||||
## Features
|
||||
|
||||
- **Model Discovery**: Auto-detect available LLM models based on API keys
|
||||
- **Role-based AI**: Create specialized AI assistants with defined expertise and personas
|
||||
- **Multiple Providers**: Support for OpenAI, Anthropic, Azure, and other LLM providers
|
||||
|
||||
## Usage
|
||||
|
||||
For example in Zed:
|
||||
|
||||
```json
|
||||
"context_servers": {
|
||||
"TheVoices": {
|
||||
"source": "custom",
|
||||
"command": {
|
||||
"path": "uvx thevoices",
|
||||
"args": [],
|
||||
"env": {
|
||||
"LITELLM_MODEL": "openai/gpt-4.1",
|
||||
"ANTHROPIC_API_KEY": "YOUR_API_KEY",
|
||||
"OPENAI_API_KEY": "YOUR_API_KEY"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Available Tools
|
||||
|
||||
#### `list_available_models()`
|
||||
Returns available LLM models based on your API keys.
|
||||
|
||||
#### `ask_the_voice(role_title, role_description, context, task, model?, temperature?)`
|
||||
Create specialized AI assistants with defined roles and expertise.
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
ask_the_voice(
|
||||
role_title="The Security Architect",
|
||||
role_description="Senior cybersecurity expert specializing in threat analysis",
|
||||
context="We detected unusual network traffic patterns...",
|
||||
task="Analyze this security incident and provide remediation steps"
|
||||
)
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.13+
|
||||
- FastMCP 2.9.2+
|
||||
- LiteLLM 1.73.6+
|
||||
- Valid API keys for at least one LLM provider
|
15
pyproject.toml
Normal file
15
pyproject.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
[project]
|
||||
name = "thevoices"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
authors = [{ name = "Tim Jagenberg", email = "tim@jagenberg.info" }]
|
||||
requires-python = ">=3.13"
|
||||
dependencies = ["fastmcp>=2.9.2", "litellm>=1.73.6"]
|
||||
|
||||
[project.scripts]
|
||||
thevoices = "thevoices.__main__:run"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
0
src/thevoices/__init__.py
Normal file
0
src/thevoices/__init__.py
Normal file
278
src/thevoices/__main__.py
Normal file
278
src/thevoices/__main__.py
Normal file
|
@ -0,0 +1,278 @@
|
|||
#!/usr/bin/env python3
|
||||
import os
|
||||
from typing import List
|
||||
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.exceptions import ToolError
|
||||
from litellm import completion, model_list
|
||||
from litellm.utils import get_llm_provider # type: ignore
|
||||
|
||||
# Create the MCP server
|
||||
mcp = FastMCP(name="TheVoicesServer")
|
||||
|
||||
|
||||
@mcp.tool
|
||||
def list_available_models() -> List[str]:
|
||||
"""
|
||||
Returns a list of available LLM models based on detected API keys in the environment.
|
||||
|
||||
Auto-discovers models from LiteLLM's model list and filters based on available API keys.
|
||||
|
||||
Returns:
|
||||
List of model names in LiteLLM format (provider/model-name).
|
||||
"""
|
||||
available_models = []
|
||||
|
||||
# Define provider to API key mapping
|
||||
provider_key_mapping = {
|
||||
"openai": "OPENAI_API_KEY",
|
||||
"anthropic": "ANTHROPIC_API_KEY",
|
||||
"vertex_ai": ["VERTEXAI_PROJECT", "VERTEXAI_LOCATION"], # Both needed
|
||||
"azure": ["AZURE_API_KEY", "AZURE_API_BASE"], # Both needed
|
||||
"cohere": "COHERE_API_KEY",
|
||||
"huggingface": "HUGGINGFACE_API_KEY",
|
||||
"replicate": "REPLICATE_API_TOKEN",
|
||||
"together_ai": "TOGETHERAI_API_KEY",
|
||||
"openrouter": "OPENROUTER_API_KEY",
|
||||
"ai21": "AI21_API_KEY",
|
||||
"palm": "PALM_API_KEY",
|
||||
"bedrock": ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"], # AWS credentials
|
||||
"sagemaker": ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"], # AWS credentials
|
||||
}
|
||||
|
||||
# Check which providers have API keys available
|
||||
available_providers = set()
|
||||
|
||||
for provider, keys in provider_key_mapping.items():
|
||||
if isinstance(keys, list):
|
||||
# All keys in the list must be present
|
||||
if all(os.environ.get(key) for key in keys):
|
||||
available_providers.add(provider)
|
||||
else:
|
||||
# Single key must be present
|
||||
if os.environ.get(keys):
|
||||
available_providers.add(provider)
|
||||
|
||||
# Filter models based on available providers
|
||||
for model in model_list:
|
||||
try:
|
||||
# Get provider for this model
|
||||
_, provider, _, _ = get_llm_provider(model)
|
||||
|
||||
# Check if we have API keys for this provider
|
||||
if provider in available_providers:
|
||||
available_models.append(f"{provider}/{model}")
|
||||
except Exception:
|
||||
# Skip models that can't be processed
|
||||
continue
|
||||
|
||||
# Add currently selected model if set
|
||||
current_model = os.environ.get("LITELLM_MODEL")
|
||||
if current_model:
|
||||
available_models.insert(0, current_model)
|
||||
|
||||
# Sort alphabetically for better readability
|
||||
available_models.sort()
|
||||
|
||||
return available_models
|
||||
|
||||
|
||||
@mcp.tool
|
||||
def ask_the_voice(
|
||||
role_title: str,
|
||||
role_description: str,
|
||||
context: str,
|
||||
task: str,
|
||||
model: str = None,
|
||||
temperature: float = None,
|
||||
) -> str:
|
||||
"""
|
||||
Role-based assistant tool for specialized AI personas and expert consultations.
|
||||
|
||||
This tool creates focused AI assistants by defining specific roles, contexts, and tasks.
|
||||
It's designed to leverage the power of role-playing to get more targeted, expert-level
|
||||
responses from language models by establishing clear personas and domains of expertise.
|
||||
|
||||
## Core Concept
|
||||
Instead of generic AI responses, this tool creates specialized "voices" - AI personas
|
||||
with distinct expertise, perspectives, and communication styles. Each voice is tailored
|
||||
to specific domains, from technical analysis to creative ideation.
|
||||
|
||||
## Parameters
|
||||
|
||||
### role_title (str) - Required
|
||||
The name/title of the AI persona, preferably with a definite article for clarity.
|
||||
|
||||
Examples:
|
||||
- "The Security Architect" - for cybersecurity analysis
|
||||
- "The UX Researcher" - for user experience insights
|
||||
- "The Data Scientist" - for statistical analysis
|
||||
- "The Creative Director" - for design and branding
|
||||
- "The Systems Analyst" - for technical architecture
|
||||
- "The Product Manager" - for feature prioritization
|
||||
|
||||
### role_description (str) - Required
|
||||
A concise but comprehensive description of the role's expertise, background, and approach.
|
||||
Should establish credibility and set expectations for the type of responses.
|
||||
|
||||
Best practices:
|
||||
- Include relevant experience/background
|
||||
- Mention key skills and methodologies
|
||||
- Define the persona's communication style
|
||||
- Establish domain expertise boundaries
|
||||
|
||||
Example: "A senior cybersecurity professional with 15+ years in enterprise security,
|
||||
specializing in threat modeling, risk assessment, and security architecture. Known for
|
||||
pragmatic, actionable advice that balances security with business needs."
|
||||
|
||||
### context (str) - Required
|
||||
Comprehensive background information for the task (can be long, up to 75% of the maximum context window).
|
||||
This is where you provide all relevant details, data, constraints, and background.
|
||||
|
||||
Should include:
|
||||
- Current situation or problem statement
|
||||
- Relevant technical details or specifications
|
||||
- Business constraints or requirements
|
||||
- Previous attempts or existing solutions
|
||||
- Success criteria or desired outcomes
|
||||
- Any relevant data, logs, or documentation
|
||||
|
||||
### task (str) - Required
|
||||
The specific request or question you want the AI persona to address.
|
||||
Should be clear, actionable, and aligned with the role's expertise.
|
||||
|
||||
Examples:
|
||||
- "Analyze this security incident and provide a remediation plan"
|
||||
- "Design a user research study for our new mobile app feature"
|
||||
- "Review this architecture proposal and identify potential issues"
|
||||
- "Develop a go-to-market strategy for our B2B SaaS product"
|
||||
|
||||
### model (str) - Optional
|
||||
Override the default LLM model. Use format: "provider/model-name"
|
||||
Use the `list_available_models` tool before using this.
|
||||
|
||||
Examples:
|
||||
- "openai/gpt-4-turbo-preview" - For complex reasoning tasks
|
||||
- "anthropic/claude-3-sonnet-20240229" - For detailed analysis
|
||||
- "openai/gpt-3.5-turbo" - For faster, cost-effective responses
|
||||
|
||||
If not specified, uses the LITELLM_MODEL environment variable.
|
||||
|
||||
### temperature (float) - Optional
|
||||
Controls response creativity and randomness (0.0-2.0).
|
||||
|
||||
Guidelines:
|
||||
- 0.0-0.3: Highly deterministic, factual responses (technical analysis, code review)
|
||||
- 0.4-0.7: Balanced creativity and consistency (general consulting, planning)
|
||||
- 0.8-1.2: More creative and varied responses (brainstorming, creative writing)
|
||||
- 1.3-2.0: Highly creative, experimental responses (artistic concepts, innovation)
|
||||
|
||||
## Returns
|
||||
str: The AI persona's response as a plain string, formatted according to the role's
|
||||
expertise and communication style.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Technical Code Review
|
||||
```python
|
||||
response = ask_the_voice(
|
||||
role_title="The Senior Software Architect",
|
||||
role_description="A seasoned software architect with expertise in scalable systems,
|
||||
clean code principles, and performance optimization. Provides detailed technical
|
||||
reviews with actionable recommendations.",
|
||||
context="We're reviewing a new microservice written in Python using FastAPI...",
|
||||
task="Review this code for scalability, security, and maintainability issues.",
|
||||
temperature=0.2
|
||||
)
|
||||
```
|
||||
|
||||
### Business Strategy Analysis
|
||||
```python
|
||||
response = ask_the_voice(
|
||||
role_title="The Strategic Business Consultant",
|
||||
role_description="MBA with 20+ years in strategy consulting, specializing in
|
||||
market analysis, competitive positioning, and growth strategies for tech companies.",
|
||||
context="Our SaaS startup is facing increased competition and slowing growth...",
|
||||
task="Develop a comprehensive competitive strategy for the next 18 months.",
|
||||
temperature=0.6
|
||||
)
|
||||
```
|
||||
|
||||
### Creative Brainstorming
|
||||
```python
|
||||
response = ask_the_voice(
|
||||
role_title="The Innovation Catalyst",
|
||||
role_description="A creative strategist and design thinker who excels at generating
|
||||
breakthrough ideas and connecting disparate concepts into innovative solutions.",
|
||||
context="We need fresh approaches to user engagement in our mobile app...",
|
||||
task="Generate 10 innovative features that could revolutionize user engagement.",
|
||||
temperature=1.0
|
||||
)
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Match Role to Task**: Ensure the persona's expertise aligns with your specific need
|
||||
2. **Rich Context**: Provide comprehensive background - more context = better responses
|
||||
3. **Specific Tasks**: Clear, actionable requests yield more useful outputs
|
||||
4. **Temperature Tuning**: Adjust creativity level based on the type of response needed
|
||||
5. **Iterative Refinement**: Use follow-up questions to dive deeper into specific areas
|
||||
|
||||
## Error Handling
|
||||
Raises ToolError for:
|
||||
- Missing or invalid model configuration
|
||||
- LLM API failures or timeouts
|
||||
- Malformed responses from the language model
|
||||
"""
|
||||
# Determine which LLM model to use
|
||||
selected_model = model or os.environ.get("LITELLM_MODEL")
|
||||
if not selected_model:
|
||||
raise ToolError(
|
||||
"Missing environment variable: LITELLM_MODEL and no model parameter provided"
|
||||
)
|
||||
|
||||
# Construct the prompt
|
||||
prompt = (
|
||||
f"You are '{role_title}'\n\n"
|
||||
"# Role Description\n\n"
|
||||
f"{role_description}\n\n"
|
||||
"# Context\n\n"
|
||||
f"{context}\n\n"
|
||||
"# Task\n\n"
|
||||
f"{task}"
|
||||
)
|
||||
|
||||
# Call the LLM via LiteLLM
|
||||
try:
|
||||
completion_params = {
|
||||
"model": selected_model,
|
||||
"messages": [
|
||||
{"role": "system", "content": prompt},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Please respond based on the above instructions.",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
# Add temperature if provided
|
||||
if temperature is not None:
|
||||
completion_params["temperature"] = temperature
|
||||
|
||||
response = completion(**completion_params)
|
||||
except Exception as e:
|
||||
raise ToolError(f"LLM request failed: {e}")
|
||||
|
||||
# Extract and return the generated content
|
||||
try:
|
||||
content = response.choices[0].message.content # type: ignore
|
||||
return content if content is not None else ""
|
||||
except (AttributeError, IndexError) as e:
|
||||
raise ToolError(f"Unexpected response format: {e}")
|
||||
|
||||
|
||||
def run():
|
||||
mcp.run()
|
||||
|
||||
|
||||
run()
|
Loading…
Reference in a new issue