Skip to content

Commit 709bf3a

Browse files
authored
examples: add multi-turn conversation with TEE verification (#210)
Shows how to maintain conversation history across turns while getting a cryptographic transaction hash for every inference step. Each turn builds on the previous context, demonstrating real-world usage patterns for stateful LLM applications on OpenGradient. Co-authored-by: ygd58 <ygd58@users.noreply.github.com>
1 parent 52f7ef0 commit 709bf3a

1 file changed

Lines changed: 120 additions & 0 deletions

File tree

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
"""
2+
Multi-turn conversation example with OpenGradient TEE-verified LLM.
3+
4+
Demonstrates how to maintain conversation history across multiple turns,
5+
enabling context-aware responses with full cryptographic verification
6+
of every inference step.
7+
8+
Usage:
9+
export OG_PRIVATE_KEY="your_private_key"
10+
python examples/llm_multi_turn_conversation.py
11+
"""
12+
13+
import asyncio
14+
import os
15+
16+
import opengradient as og
17+
18+
19+
def add_user_message(history: list, content: str) -> list:
20+
"""Append a user message to the conversation history."""
21+
return history + [{"role": "user", "content": content}]
22+
23+
24+
def add_assistant_message(history: list, content: str) -> list:
25+
"""Append an assistant message to the conversation history."""
26+
return history + [{"role": "assistant", "content": content}]
27+
28+
29+
async def chat_turn(
30+
llm: og.LLM,
31+
history: list,
32+
user_input: str,
33+
model: og.TEE_LLM = og.TEE_LLM.GEMINI_2_5_FLASH,
34+
) -> tuple[str, list, str]:
35+
"""
36+
Execute a single conversation turn.
37+
38+
Args:
39+
llm: Initialized LLM client.
40+
history: Current conversation history.
41+
user_input: The user's message for this turn.
42+
model: TEE_LLM model to use.
43+
44+
Returns:
45+
Tuple of (assistant_reply, updated_history, transaction_hash).
46+
"""
47+
history = add_user_message(history, user_input)
48+
49+
result = await llm.chat(
50+
model=model,
51+
messages=history,
52+
max_tokens=500,
53+
temperature=0.7,
54+
)
55+
56+
reply = result.chat_output["content"]
57+
history = add_assistant_message(history, reply)
58+
59+
return reply, history, result.transaction_hash
60+
61+
62+
async def main():
63+
private_key = os.environ.get("OG_PRIVATE_KEY")
64+
if not private_key:
65+
raise ValueError("OG_PRIVATE_KEY environment variable is not set.")
66+
67+
llm = og.LLM(private_key=private_key)
68+
llm.ensure_opg_approval(min_allowance=0.5)
69+
70+
model = og.TEE_LLM.GEMINI_2_5_FLASH
71+
print(f"Model : {model.value}")
72+
print(f"Mode : Multi-turn conversation with TEE verification")
73+
print("=" * 60)
74+
75+
# System prompt sets the assistant persona for the whole conversation
76+
history = [
77+
{
78+
"role": "system",
79+
"content": (
80+
"You are a concise Python tutor. "
81+
"Give short, clear answers with code examples when helpful."
82+
),
83+
}
84+
]
85+
86+
# --- Turn 1 ---
87+
question_1 = "What is a Python decorator?"
88+
print(f"\nUser : {question_1}")
89+
90+
reply_1, history, tx_1 = await chat_turn(llm, history, question_1, model)
91+
print(f"Assistant : {reply_1}")
92+
print(f"[tx: {tx_1}]")
93+
94+
# --- Turn 2 — follow-up referencing Turn 1 ---
95+
question_2 = "Can you show me a real-world example of one?"
96+
print(f"\nUser : {question_2}")
97+
98+
reply_2, history, tx_2 = await chat_turn(llm, history, question_2, model)
99+
print(f"Assistant : {reply_2}")
100+
print(f"[tx: {tx_2}]")
101+
102+
# --- Turn 3 — deeper follow-up ---
103+
question_3 = "How would I stack two decorators on the same function?"
104+
print(f"\nUser : {question_3}")
105+
106+
reply_3, history, tx_3 = await chat_turn(llm, history, question_3, model)
107+
print(f"Assistant : {reply_3}")
108+
print(f"[tx: {tx_3}]")
109+
110+
# Summary
111+
print("\n" + "=" * 60)
112+
print(f"Total turns : {len([m for m in history if m['role'] == 'user'])}")
113+
print(f"Total messages : {len(history)}")
114+
print("Transaction hashes (verifiable on-chain):")
115+
for i, tx in enumerate([tx_1, tx_2, tx_3], 1):
116+
print(f" Turn {i}: {tx}")
117+
118+
119+
if __name__ == "__main__":
120+
asyncio.run(main())

0 commit comments

Comments
 (0)