Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ dependencies = [
"rich>=13.0",
"datasets>=2.19.0",
"huggingface_hub>=0.23.0",
"fastapi>=0.110",
"uvicorn>=0.29",
"websockets>=12",
]

[project.optional-dependencies]
dev = ["pytest>=8.0", "ruff>=0.4", "jinja2>=3.1"]
studio = ["fastapi>=0.110", "uvicorn>=0.29", "websockets>=12"]
dev = ["pytest>=8.0", "ruff>=0.4", "jinja2>=3.1", "httpx>=0.27"]

[project.scripts]
teich = "teich.cli:main"
Expand Down
57 changes: 57 additions & 0 deletions src/teich/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

from datetime import datetime, timezone
from pathlib import Path
import sys
from threading import Event, RLock, Thread
from typing import Any

import typer
from huggingface_hub import HfApi
Expand Down Expand Up @@ -671,6 +673,61 @@ def init(
developer_instructions: null
'''


def _configure_studio_event_loop_policy(
*,
platform: str = sys.platform,
asyncio_module: Any | None = None,
) -> bool:
if platform != "win32":
return False
if asyncio_module is None:
import asyncio as asyncio_module

policy_cls = getattr(asyncio_module, "WindowsSelectorEventLoopPolicy", None)
if policy_cls is None:
return False
asyncio_module.set_event_loop_policy(policy_cls())
return True


@app.command()
def studio(
path: Path = typer.Argument(Path("."), help="Project directory (created/initialized if needed)"),
host: str = typer.Option("127.0.0.1", "--host", help="Host to bind the studio server to"),
port: int = typer.Option(8420, "--port", help="Port to bind the studio server to"),
open_browser: bool = typer.Option(True, "--open/--no-open", help="Open the studio in your browser"),
) -> None:
"""Launch the Teich Studio browser UI."""
import threading
import webbrowser

_configure_studio_event_loop_policy()

try:
import uvicorn

from .studio.server import create_app
except ImportError as exc:
console.print(f"[red]Teich Studio requires fastapi and uvicorn: {exc}[/red]")
console.print("Install them with: [cyan]pip install 'teich[studio]'[/cyan]")
raise typer.Exit(1)

project_dir = path.resolve()
studio_app = create_app(project_dir)
url = f"http://{host}:{port}"

console.print(Panel.fit("Teich Studio", style="bold blue"))
console.print(f"[green]Project:[/green] {project_dir}")
console.print(f"[green]Studio running at:[/green] [bold cyan]{url}[/bold cyan]")
console.print("[dim]Press Ctrl+C to stop.[/dim]")

if open_browser:
threading.Timer(0.8, lambda: webbrowser.open(url)).start()

uvicorn.run(studio_app, host=host, port=port, log_level="warning")


PROMPTS_TEMPLATE = '''{"prompt":"Build a simple todo list app in React"}
{"prompt":"Create a Python script that fetches weather data from an API"}
{"github_repo":"armand0e/perplexica-mcp","prompt":"Add a small usability improvement and update the tests"}
Expand Down
1 change: 1 addition & 0 deletions src/teich/studio/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Teich Studio: a local browser UI for configuring and steering dataset generation."""
Loading
Loading