DEV Community

Cover image for Building DriftScript: An AI Telephone Game with Streamlit, Multi-Provider LLM Routing, and Drift Scoring
Harish Kotra (he/him)
Harish Kotra (he/him)

Posted on

Building DriftScript: An AI Telephone Game with Streamlit, Multi-Provider LLM Routing, and Drift Scoring

If most LLM apps are search engines, DriftScript is improv theater.

It takes one prompt, routes it through multiple AI personalities, and surfaces how language drifts over time. The objective is not perfect fidelity. The objective is controlled chaos that people want to share.

This post breaks down how the app was built, the architecture decisions behind it, and where to take it next.

Product Idea in One Line

Input prompt -> 5 to 10 personality rewrites -> compare start and end -> score the drift.

Why This Pattern Works for Viral UX

  • It is instantly understandable
  • It is inherently replayable
  • It produces surprising outputs quickly
  • It generates shareable artifacts without extra user work

Stack

  • Streamlit for fast product iteration and deployment simplicity
  • Python for orchestration and deterministic chain logic
  • OpenAI SDK as the single API layer
  • OpenAI, Featherless, and Ollama via provider abstraction
  • Pillow for PNG share card export
  • python-dotenv for environment configuration

High-Level System Design

High-Level System Design

Provider Abstraction: One Interface, Many Backends

Using the OpenAI-compatible SDK surface lets us swap endpoints with minimal changes.

def build_client(provider: str) -> OpenAI:
    if provider == "openai":
        return OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
    if provider == "featherless":
        return OpenAI(
            api_key=os.getenv("FEATHERLESS_API_KEY"),
            base_url=os.getenv("FEATHERLESS_BASE_URL", "https://api.featherless.ai/v1"),
        )
    if provider == "ollama":
        return OpenAI(
            api_key=os.getenv("OLLAMA_API_KEY", "ollama"),
            base_url=os.getenv("OLLAMA_BASE_URL", "http://localhost:11434/v1"),
        )
Enter fullscreen mode Exit fullscreen mode

This pattern gives three immediate benefits:

  • Lower vendor lock-in
  • Cheap experimentation with model mixes
  • Same core app logic for cloud and local modes

Prompt Contract Per Step

Every agent step uses the same contract, with personality injected into the system prompt.

SYSTEM:
You are a rewriting agent with the following personality:
[PERSONALITY DESCRIPTION]
...
Rules:
- Do NOT explain
- Do NOT mention you are an AI
- Keep it concise (max 3–5 sentences)
- Amplify tone and style significantly

USER:
Rewrite this text:
[INPUT TEXT]
Enter fullscreen mode Exit fullscreen mode

Keeping the contract fixed is important. It makes output format stable while still allowing major stylistic divergence.

Chain Orchestration

The orchestrator carries forward output from one step to the next.

def run_chain(input_text, steps, provider, default_model, model_mode, random_model_pool, chaos_mode, seed):
    results = []
    current_text = input_text
    rng = random.Random(seed)

    for i in range(steps):
        personality_name, personality_desc = choose_personality(i, rng)
        model = resolve_step_model(provider, default_model, model_mode, random_model_pool, i, rng)
        step = rewrite_step(current_text, personality_name, personality_desc, provider, model, chaos_mode, seed, i)
        results.append(step)
        current_text = step.output_text

    return results, current_text
Enter fullscreen mode Exit fullscreen mode

Chaos Mode Design

Chaos mode is not random noise. It is bounded unpredictability.

What changes when enabled:

  • Base temperature increases
  • Temperature gets jitter per step
  • Extra prompt directives are sampled from a chaos instruction pool
  • Remix uses a fresh seed while preserving baseline config

This keeps outputs unstable enough to be fun, but still coherent enough to read/share.

Reliability: Retry Once

Each step retries one time on transient errors.

for attempt in range(2):
    try:
        return llm_call(...)
    except Exception:
        if attempt == 0:
            time.sleep(0.35)
        else:
            raise
Enter fullscreen mode Exit fullscreen mode

This reduces failure rate without introducing heavy queueing or backoff complexity in MVP stage.

Drift Metric

A semantic score would require embedding calls and extra cost. For MVP speed, DriftScript uses:

  • token cosine similarity
  • length ratio
  • weighted blend
preservation = (0.7 * cosine) + (0.3 * len_ratio)
drift = (1.0 - preservation) * 100
Enter fullscreen mode Exit fullscreen mode

Why this is good enough:

  • cheap and fast
  • interpretable
  • responsive in the UI

UI Decisions

The interface is structured around three moments:

  1. Run
  2. Compare
  3. Share

Key sections:

  • Sidebar configuration (provider, model routing, chaos, steps, seed)
  • Before vs After visual with lightweight word diff
  • Chain timeline in expanders for readability
  • Share card (text and PNG export)
  • Remix for quick iteration loops

Local State Features

MVP includes session-only state for:

  • run history
  • leaderboard

This is deliberate. It validates engagement loops before adding database/auth complexity.

Security and Config

Environment config is loaded from .env via python-dotenv.

Example:

OPENAI_API_KEY=
FEATHERLESS_API_KEY=
FEATHERLESS_BASE_URL=https://api.featherless.ai/v1
OLLAMA_BASE_URL=http://localhost:11434/v1
OLLAMA_API_KEY=ollama
Enter fullscreen mode Exit fullscreen mode

.env is ignored in Git.

Performance Notes

Targeting <5s for full chain depends on provider latency and model size.

To improve perceived speed next:

  • stream tokens for each step
  • parallel speculative branches then choose best output
  • cache repeated runs by seed+prompt+config

Tradeoffs Taken

  • No persistent DB yet
  • No authentication yet
  • No model-level cost analytics yet
  • No distributed queueing yet

These are intentional omissions to optimize for fast iteration and product learning.

Extension Roadmap

  • Multiplayer lobbies and real-time chain playback
  • Global public gallery and ranking signals
  • Embedding-based drift analytics
  • Team mode: alternate human + AI turns
  • Scheduled challenges and daily prompt themes
  • Fine-grained moderation layers per provider

Developer Takeaway

DriftScript demonstrates a useful architecture pattern:

  • strict prompt contract
  • pluggable model providers
  • deterministic orchestration with optional chaos
  • thin yet meaningful scoring layer
  • sharing-first UX

For small AI product teams, this is a practical blueprint for shipping social AI apps quickly without over-engineering.

Demo Output

Github Repo: https://github.com/harishkotra/DriftScript

Top comments (0)