CSE Roadmap

Core tier · about 4 hours · 7 parts

Part 6 of 7 · 3 min read

Exercises

Try each exercise before opening its hint.

Easy

Easy 1: Write the request (15 minutes)

Write by hand the messages list for a three-turn conversation with a system prompt "answer in one sentence". Then add the fourth user message. Count the messages sent on each of the four calls.

Done when: your counts are 2, 4, 6 and 8.

Hint

the system prompt counts as a message in this style.

Easy 2: Prove statelessness (15 minutes)

Using FakeLLM, make two separate calls: introduce yourself in the first and ask your name in the second. Then do it with a Conversation.

Done when: the first approach fails and the second succeeds, and you can explain why.

Easy 3: Read the stop reason (15 minutes)

Call create with max_tokens=2 and print the stop reason. Explain in two sentences what could go wrong if your app ignored it when asking for JSON.

Done when: you show the max_tokens result and name the failure (truncated, unparseable JSON).

Medium

Medium 1: Ordering rules (45 minutes)

Extend Conversation with a validate(messages) function that checks a list from outside: only known roles, a system message only first, user and assistant alternating, last message from the user. Write 8 tests, each with one specific violation.

Done when: eight tests pass and each error message names the position of the problem.

Hint

return a list of problems rather than raising on the first.

Medium 2: Pinned messages (60 minutes)

Add a pin option: messages marked as pinned (for example the user's stated preferences) are never trimmed. Test that pinned messages survive when the budget is tight and that you raise a clear error when pinned messages alone exceed the budget.

Done when: both behaviours are tested.

Hint

when trimming, skip pinned messages, but keep pairs consistent.

Medium 3: Summarise instead of drop (60 minutes)

Write compact(conversation, llm, keep_last=2) that replaces everything except the last two exchanges with a single summary message produced by a function you pass in. Check that the size falls, the newest two exchanges are intact, and the roles still alternate.

Done when: the three checks are tests, using a fake summariser.

Hint

put the summary in a user message such as "Summary so far: ..." followed by a short assistant acknowledgement, so roles alternate.

Hard

Hard 1: Provider adapter (90 minutes)

Write functions to_openai_style(conv) and to_system_field_style(conv) that produce two request shapes: system as the first message, and system as a separate field with only user and assistant messages in the list. Write a round-trip test that converts each back into a Conversation and compares.

Done when: both directions are tested and no information is lost.

Hard 2: A guard that is hard to evade (120 minutes)

Extend guard_output to catch a secret in simple disguises: different case, spaces between letters (M A N G O), and hyphenated (M-A-N-G-O). Write tests for each disguise and for a harmless reply that must pass. Then write down one disguise your guard still misses and why that matters.

Done when: disguise tests pass, there is no false block on harmless text, and the limitation is documented.

Hint

normalise by removing everything that is not a letter or digit before comparing.

Hard 3: Budget planner (120 minutes)

Write a function that, given the context limit, a maximum output size, the system prompt and the history, decides what to trim so that input plus output fits, and returns both the trimmed request and a report (dropped messages, tokens before and after). Property-test it: for 200 random histories, the result always fits, always keeps the system prompt, and always ends with the newest user message.

Done when: the property test passes for all 200 cases.

Hint

use random.Random(seed) in the test so failures are reproducible. The fixed trim already refuses an impossible budget; decide what your planner does in that case.