Add CLI commands (init, run, test, prompt)
This commit is contained in:
84
src/promptforge/cli/commands/run.py
Normal file
84
src/promptforge/cli/commands/run.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import asyncio
|
||||||
|
import click
|
||||||
|
from pathlib import Path
|
||||||
|
from promptforge.core.prompt import Prompt
|
||||||
|
from promptforge.core.template import TemplateEngine
|
||||||
|
from promptforge.core.config import get_config
|
||||||
|
from promptforge.providers import ProviderFactory
|
||||||
|
from promptforge.testing.validator import Validator
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.argument("name")
|
||||||
|
@click.option("--provider", "-p", help="Override provider")
|
||||||
|
@click.option("--var", "-v", multiple=True, help="Variables in key=value format")
|
||||||
|
@click.option("--output", "-o", type=click.Choice(["text", "json", "yaml"]), default="text")
|
||||||
|
@click.option("--stream/--no-stream", default=True, help="Stream response")
|
||||||
|
@click.pass_obj
|
||||||
|
def run(ctx, name: str, provider: str, var: tuple, output: str, stream: bool):
|
||||||
|
"""Run a prompt with variables."""
|
||||||
|
prompts_dir = ctx["prompts_dir"]
|
||||||
|
prompts = Prompt.list(prompts_dir)
|
||||||
|
|
||||||
|
prompt = next((p for p in prompts if p.name == name), None)
|
||||||
|
if not prompt:
|
||||||
|
click.echo(f"Prompt '{name}' not found", err=True)
|
||||||
|
raise click.Abort()
|
||||||
|
|
||||||
|
variables = {}
|
||||||
|
for v in var:
|
||||||
|
if "=" in v:
|
||||||
|
k, val = v.split("=", 1)
|
||||||
|
variables[k.strip()] = val.strip()
|
||||||
|
|
||||||
|
template_engine = TemplateEngine()
|
||||||
|
try:
|
||||||
|
rendered = template_engine.render(prompt.content, variables, prompt.variables)
|
||||||
|
except Exception as e:
|
||||||
|
click.echo(f"Template error: {e}", err=True)
|
||||||
|
raise click.Abort()
|
||||||
|
|
||||||
|
config = get_config()
|
||||||
|
selected_provider = provider or prompt.provider or config.defaults.provider
|
||||||
|
|
||||||
|
try:
|
||||||
|
provider_instance = ProviderFactory.create(
|
||||||
|
selected_provider,
|
||||||
|
model=config.providers.get(selected_provider, {}).model if selected_provider in config.providers else None,
|
||||||
|
temperature=config.providers.get(selected_provider, {}).temperature if selected_provider in config.providers else 0.7,
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
click.echo(f"Provider error: {e}", err=True)
|
||||||
|
raise click.Abort()
|
||||||
|
|
||||||
|
async def execute():
|
||||||
|
if stream:
|
||||||
|
full_response = []
|
||||||
|
async for chunk in provider_instance.stream_complete(rendered):
|
||||||
|
click.echo(chunk, nl=False)
|
||||||
|
full_response.append(chunk)
|
||||||
|
response = "".join(full_response)
|
||||||
|
else:
|
||||||
|
result = await provider_instance.complete(rendered)
|
||||||
|
response = result.content
|
||||||
|
click.echo(response)
|
||||||
|
|
||||||
|
if output == "json":
|
||||||
|
import json
|
||||||
|
click.echo("\n" + json.dumps({"response": response}, indent=2))
|
||||||
|
|
||||||
|
asyncio.run(execute())
|
||||||
|
|
||||||
|
|
||||||
|
def validate_response(prompt: Prompt, response: str):
|
||||||
|
for rule in prompt.validation_rules:
|
||||||
|
if rule.type == "regex":
|
||||||
|
import re
|
||||||
|
if not re.search(rule.pattern or "", response):
|
||||||
|
click.echo(f"Warning: Response failed regex validation", err=True)
|
||||||
|
elif rule.type == "json":
|
||||||
|
try:
|
||||||
|
import json
|
||||||
|
json.loads(response)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
click.echo(f"Warning: Response is not valid JSON", err=True)
|
||||||
Reference in New Issue
Block a user