PyRITpyrit 1.1.0 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
21 small wins to finish your pathNext lesson

The memory every run writes to

Lesson 3 installed PyRIT. Before anything can be sent, PyRIT needs somewhere to write it down, and it will not let you skip that step.

Every prompt, every reply and every score goes into a database. Starting PyRIT means choosing which one.

Example
from pyrit.setup import IN_MEMORY, initialize_pyrit_async

await initialize_pyrit_async(memory_db_type=IN_MEMORY, silent=True)
print("ready")

IN_MEMORY is a SQLite database that lives in the process and disappears when it ends, which is what every lesson here wants. Lesson 20 swaps it for one on disk.

silent=True is not cosmetic. Without it the call prints two lines of start-up banner to standard output, and one of those two lines only appears the first time you start PyRIT in a given process. Anything comparing output between runs breaks on that.

The memory object

One call gets you the database. It is a process-wide single object, which matters in a moment.

Example
from pyrit.memory import CentralMemory

db = CentralMemory.get_memory_instance()
print(type(db).__name__)

An in-memory choice still gives you SQLiteMemory; the only difference is that the file never touches the disk. Everything PyRIT records goes through this one object.

Putting a row in by hand

Nothing writes to memory on its own; an attack will do it from lesson 6. To have something to look at now, add one message yourself. It needs a conversation to belong to, and PyRIT will not invent one for you.

Example
from pyrit.models import Message

note = Message.from_prompt(prompt="hello", role="user")
try:
    db.add_message_to_memory(request=note)
except ValueError as e:
    print("ValueError:", str(e).partition(" has ")[2])

Every row in the database belongs to a conversation, because that is how a reply is later matched to the prompt that caused it. Set one and the write goes through.

Starting PyRIT again does not empty it

This one costs people an afternoon. CentralMemory hands back the same object every time, and calling initialize_pyrit_async a second time does not replace it, so yesterday's rows are still there.

Example
note.get_piece().conversation_id = "probe-1"
db.add_message_to_memory(request=note)
print("before:", len(db.get_message_pieces()))

await initialize_pyrit_async(memory_db_type=IN_MEMORY, silent=True)
again = CentralMemory.get_memory_instance()
print("after starting again:", len(db.get_message_pieces()))
print("same object?", again is db)

The row survived. If you have ever printed everything in memory and found rows from a run you thought you had thrown away, this is why.

The reset, and a helper worth having

Example
db.reset_database()
print("after reset:", len(db.get_message_pieces()))

Starting silently and then emptying the database is the opening of every lesson from here on, so it is worth a name. Put this in pretend_pyrit.py; the rest of the file arrives in lesson 5.

Example
async def arena():
    await initialize_pyrit_async(memory_db_type=IN_MEMORY, silent=True)
    memory = CentralMemory.get_memory_instance()
    memory.reset_database()
    return memory
Try it yourself
  • Delete silent=True and run the first snippet twice in one session. The two outputs differ.
  • Add two messages, reset, and check the count is zero.
  • Call db.print_schema() and look at the tables PyRIT keeps.

This is what real progress feels like.