Absolute Path

Model APIs cheat sheet

The calls and the rules of the course on one page. The code uses sim_model, the simulated model of the lessons.

A call

Lesson: Your first call to a model

from sim_model import Client

client = Client(api_key="test-key-lesson")
text = "Summarize this: The dock was full."
messages = [{"role": "user", "content": text}]

response = client.chat(messages)
print(response["content"])
Key of the answer Value
content the text that the model wrote
stop_reason why the model stopped. "end" is a complete answer.
usage input_tokens and output_tokens
model the name of the model that answered
id a name for this call, for a search in a log
latency_ms the time of the answer, in milliseconds

Roles and the system message

Lesson: Roles and the system message

RULES = "Summarize the text in 8 words."
comment = "The dock at Town Hall was full."

messages = [
    {"role": "system", "content": RULES},
    {"role": "user", "content": comment},
]
Role Who writes it What it holds
system you, the developer the job, the rules, the form of the answer
user a person, or your program the text for this call
assistant the model an answer that the model gave before

Say what you want: the labels that you allow, and the form of the answer.

Tokens and limits

Lesson: Tokens and limits

from sim_model import MODELS, count_tokens

count_tokens("The dock was full.")  # 5
MODELS["sim-small"]["context"]      # 300
MODELS["sim-large"]["context"]      # 1500

response = client.chat(messages, max_tokens=5)
response["stop_reason"]  # "max_tokens"
  • A model reads and writes tokens. You pay for each token, and each call has a limit.
  • The context is the limit of the input. Messages above it give a BadRequestError.
  • max_tokens is the limit of the output. The stop reason "max_tokens" means that the service cut the answer.

The cost of a call

Lesson: The cost of a call

from sim_model import MODELS, cost

price = MODELS["sim-small"]
dollars = (
    2000 * price["input_price"]    # input tokens
    + 500 * price["output_price"]  # output tokens
) / 1_000_000                      # 0.001

usage = {"input_tokens": 2000, "output_tokens": 500}
cost(usage)                     # 0.001
cost(usage, model="sim-large")  # 0.012

A price is for 1,000,000 tokens. Output tokens cost more than input tokens. The prices in this course are made up.

To lower the cost: use the smallest model that does the job well, keep the instructions short, ask for a short answer, set max_tokens, and do not ask twice for the same text.

Conversations

Lesson: Conversations

history = [
    {"role": "user", "content": "My name is Rosa."},
    {"role": "assistant", "content": "Hello."},
    {"role": "user", "content": "What is my name?"},
]
response = client.chat(history)
answer = response["content"]
history.append(
    {"role": "assistant", "content": answer}
)
  • The model does not remember a call. Your program sends the conversation again each time.
  • Each turn sends all earlier turns, so each turn costs more than the turn before.
  • Keep the list short, for example with only the last turns.

Temperature and steady answers

Lesson: Temperature and steady answers

# Steady: for each answer that a program reads
client.chat(messages, temperature=0)
# With variety: for ideas and drafts
client.chat(messages, temperature=0.8)

# Normalize an answer before you compare it
answer = " Complaint.\n"
answer.strip().removesuffix(".").lower()
# 'complaint'

Answers as JSON

Lesson: Answers as JSON

import json

STRICT = (
    "Classify the comment as one of: praise, "
    "complaint, question, other. Answer with only "
    'JSON. Use the keys "label" and "reason".'
)

text = '{"label": "complaint", "reason": "Full."}'
data = json.loads(text)
data["label"]  # 'complaint'

# Cut away the text around the JSON
text = 'Here is the JSON:\n{"label": "praise"}'
start = text.find("{")
end = text.rfind("}")
json.loads(text[start:end + 1])
# {'label': 'praise'}

Strict instructions name three things: only JSON, the keys, and the values that you allow. json.loads raises a ValueError for text that is not JSON.

Check the answer, and try again

Lesson: Check the answer, and try again

LABELS = [
    "praise", "complaint", "question", "other",
]

def is_good(data):
    if not isinstance(data, dict):
        return False
    if "label" not in data:
        return False
    return data["label"] in LABELS

data = None
for attempt in range(3):
    response = client.chat(messages)
    try:
        answer = json.loads(response["content"])
    except ValueError:
        continue  # not JSON: ask again
    if is_good(answer):
        data = answer
        break

Set a limit for the tries. When no try works, use a default value, give the item to a person, or stop with an error. Do not go on with an answer that failed the check.

When a call fails

Lesson: When a call fails

Error Cause Does a new try help?
AuthError the key is missing or wrong no
BadRequestError the call is wrong no
RateLimitError too many calls in a short time yes, after a wait
ServerError the service has a problem yes, after a wait
ModelTimeout the answer did not come in time yes
from sim_model import ModelError, RateLimitError

try:
    response = client.chat(messages)
except RateLimitError as error:
    print("Wait for", error.retry_after, "seconds.")
except ModelError as error:
    print(type(error).__name__, str(error))

ModelError is the parent class of the five errors. Put a special class before its parent class.

Try again with a wait

Lesson: Try again with a wait

from sim_model import ServerError, wait

def backoff(attempt):
    # 1, 2, 4, 8, 16, and then 30 seconds
    return min(2 ** attempt, 30)

def chat_with_retry(messages, max_attempts):
    for attempt in range(max_attempts):
        try:
            return client.chat(messages)
        except ServerError:
            if attempt == max_attempts - 1:
                raise
            wait(backoff(attempt))
  • The wait doubles after each failure. This is exponential backoff.
  • For a RateLimitError, wait for error.retry_after seconds.
  • Stop after some attempts, and raise the last error.
  • Try again only for an error that a new try can fix.

Keys and settings

Lesson: Keys and settings

import os

key = os.environ.get("MODEL_API_KEY", "")
if key == "":
    raise RuntimeError("Set MODEL_API_KEY.")

model = os.environ.get("MODEL_NAME", "sim-small")
  • A key is a secret. Keep it out of the code, of messages, of the output, and of a web page.
  • To show which key is in use, show only its last 4 characters.
  • When a key got out, delete it in the account and make a new key.
  • Set a spending limit, and use one key for each program.
  • Do not type a real key into a lesson.

Many items

Lesson: Many items

def process(comment):
    text = comment["text"]
    messages = [
        {"role": "system", "content": STRICT},
        {"role": "user", "content": text},
    ]
    try:
        response = client.chat(messages)
    except ModelError as error:
        name = type(error).__name__
        return {"id": comment["id"], "error": name}
    label = json.loads(response["content"])["label"]
    return {"id": comment["id"], "label": label}

results = [process(comment) for comment in comments]

with open("results.jsonl", "w",
          encoding="utf-8") as file:
    for row in results:
        file.write(json.dumps(row) + "\n")

One item gives one row: a result, or the name of the error. Then one failed call does not stop the run, and you know which items have no answer. Add the tokens and the cost of each row to see the totals of the run.

Choose a model

Lesson: Choose a model

response = client.chat(messages, model="sim-large")

# Accuracy on a test set, in percent
right = 0
for example in examples:
    answer = classify(example["text"])
    if answer == example["label"]:
        right += 1
accuracy = 100 * right / len(examples)
  • A test set is a list of examples with the right answer for each one.
  • Read the wrong answers. A group of the same kind shows a weak point of the model.
  • Choose with two questions: what does a wrong answer cost, and how many calls does the job make?

A real service

Lesson: From the simulation to a real service

Status code Meaning Error in this course
400 the call is wrong BadRequestError
401 the key is missing or wrong AuthError
429 too many calls or tokens RateLimitError
500, 503 the service has a problem ServerError
  • Each service has its own package. The names of the client, the method and the parameters are different.
  • Put the call to the package in one small function of your own.
  • Send private data only when the rules for the data allow it.
  • Calculate the cost of a large run before you start it.
  • Set a time limit for each call.
  • Keep the name of the model in a setting, and run your test set again when you change the model.