DEV Community

shashank ms
shashank ms

Posted on

Building a Text Generation Tool with LLM

We are going to build a CLI tool that turns a rough topic and a target audience into a structured technical blog draft. It is useful for developer advocates, tech leads, and anyone who needs to ship prose faster without leaving the terminal. We will run it against Oxlo.ai because its flat per-request pricing keeps costs predictable even when we send long system prompts; see https://oxlo.ai/pricing for details.

What you'll need

Python 3.10 or newer. The OpenAI SDK installed with pip install openai. An Oxlo.ai API key from https://portal.oxlo.ai.

Step 1: Verify the client and endpoint

Create a file named draft_generator.py. Start with the Oxlo.ai client and make a quick call to confirm your key and the endpoint are working. I use llama-3.3-70b here because it is a reliable general-purpose model.

from openai import OpenAI

client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")

response = client.chat.completions.create(
    model="llama-3.3-70b",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Say hello"},
    ],
)

print(response.choices[0].message.content)

Run it with python draft_generator.py. If you see a greeting, the pipe is open.

Step 2: Lock in the system prompt

Replace the placeholder system message with a strict prompt that forces the model to return Markdown with front matter and an outline. Keeping this prompt in a dedicated constant makes it easy to iterate.

SYSTEM_PROMPT = '''You are a technical writer. Given a topic and an audience level, produce a blog draft in Markdown.

Rules:
- Start with YAML front matter containing title, audience, and estimated_reading_time.
- Write an introduction of no more than three paragraphs.
- Provide a bullet outline for the body sections.
- End with a one-paragraph conclusion.
- Be concise and concrete.
'''

Step 3: Build the generator function

Add a function that accepts the topic and audience, injects them into the user message, and returns the generated string. I keep the print statements minimal so the function stays portable if I later wrap it into a FastAPI endpoint.

from openai import OpenAI

client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")

SYSTEM_PROMPT = '''You are a technical writer. Given a topic and an audience level, produce a blog draft in Markdown.

Rules:
- Start with YAML front matter containing title, audience, and estimated_reading_time.
- Write an introduction of no more than three paragraphs.
- Provide a bullet outline for the body sections.
- End with a one-paragraph conclusion.
- Be concise and concrete.
'''

def generate_draft(topic: str, audience: str) -> str:
    user_message = f"Topic: {topic}\nAudience: {audience}"
    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_message},
        ],
    )
    return response.choices[0].message.content

if __name__ == "__main__":
    draft = generate_draft("request-based pricing for LLMs", "senior engineers")
    print(draft)

Step 4: Stream the response

Waiting for the full draft can take a few seconds. Switching to streaming lets us watch the text arrive in real time. We only need to change the API call and the consumption loop.

from openai import OpenAI

client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")

SYSTEM_PROMPT = '''You are a technical writer. Given a topic and an audience level, produce a blog draft in Markdown.

Rules:
- Start with YAML front matter containing title, audience, and estimated_reading_time.
- Write an introduction of no more than three paragraphs.
- Provide a bullet outline for the body sections.
- End with a one-paragraph conclusion.
- Be concise and concrete.
'''

def generate_draft(topic: str, audience: str) -> None:
    user_message = f"Topic: {topic}\nAudience: {audience}"
    stream = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_message},
        ],
        stream=True,
    )
    for chunk in stream:
        content = chunk.choices[0].delta.content
        if content:
            print(content, end="", flush=True)
    print()

if __name__ == "__main__":
    generate_draft("request-based pricing for LLMs", "senior engineers")

Step 5: Add a proper CLI with argparse

Hardcoding topic strings is fine for a spike, but a real tool needs flags. Let us add argparse so we can reuse the script across different posts without editing the source.

import argparse
from openai import OpenAI

client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")

SYSTEM_PROMPT = '''You are a technical writer. Given a topic and an audience level, produce a blog draft in Markdown.

Rules:
- Start with YAML front matter containing title, audience, and estimated_reading_time.
- Write an introduction of no more than three paragraphs.
- Provide a bullet outline for the body sections.
- End with a one-paragraph conclusion.
- Be concise and concrete.
'''

def generate_draft(topic: str, audience: str) -> None:
    user_message = f"Topic: {topic}\nAudience: {audience}"
    stream = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_message},
        ],
        stream=True,
    )
    for chunk in stream:
        content = chunk.choices[0].delta.content
        if content:
            print(content, end="", flush=True)
    print()

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Generate a technical blog draft via Oxlo.ai")
    parser.add_argument("--topic", required=True, help="Blog topic")
    parser.add_argument("--audience", required=True, help="Target audience")
    args = parser.parse_args()
    generate_draft(args.topic, args.audience)

Run it

Call the finished script from the terminal.

python draft_generator.py --topic "request-based pricing for LLMs" --audience "senior engineers"

The tool streams back a draft that looks something like this.

---
title: "Request-Based Pricing for LLMs"
audience: senior engineers
estimated_reading_time: 6 minutes
---

Introduction

Token-based billing is the default in AI inference, but it penalizes long prompts and multi-turn agents. Request-based pricing flips the model: you pay once per API call, no matter how large the context.

For teams running retrieval-augmented generation or autonomous agent loops, this removes the cost surprise that comes from stuffing a knowledge base into every prompt. You can optimize for quality instead of token economy.

Outline

- How token meters inflate costs on long-context workloads
- Where request-based pricing wins: RAG, agents, and batch jobs
- A concrete example comparing prompt growth over a session
- Migration tips: moving from token-based to per-request billing
- Monitoring and capacity planning without token math

Conclusion

Request-based pricing simplifies budgeting and removes the friction between context size and cost. If your prompts are growing and your bill is accelerating, it is worth evaluating a flat-per-request provider.

Wrap-up

Two concrete ways to push this further. First, add a second pass: after the outline prints, capture user approval and call the model again with the previous assistant message in the thread plus a user message like Expand each bullet into two paragraphs. Second, package the script with a Typer CLI and publish an internal wheel. Because Oxlo.ai is fully OpenAI SDK compatible, the only change needed to scale up is swapping the model string to deepseek-v3.2 or kimi-k2.6 when you need stronger reasoning.

Top comments (0)