feat: add conversation fetch code snippets with pagination support

- Add pagination to GET /api/agents/[id]/conversation/[conversationId]
  - limit and offset query params (default: 50/0, max: 100)
  - Returns hasMore in pagination object
- Add "Fetch Conversations" section on agent details page
  - cURL, TypeScript, Python examples
  - Shows list, get, and delete endpoints
  - Demonstrates pagination handling
This commit is contained in:
Ajax Davis 2026-01-07 19:47:34 +10:00
parent d156b29286
commit 3f5c1bda61
2 changed files with 182 additions and 9 deletions

View file

@ -392,10 +392,18 @@ export async function POST(request: NextRequest, context: RouteContext): Promise
/**
* GET /api/agents/[id]/conversation/[conversationId]
* Retrieve conversation history (accepts id or uid)
* Retrieve conversation history with pagination (accepts id or uid)
*
* Query params:
* - limit: Max messages to return (default: 50, max: 100)
* - offset: Number of messages to skip (default: 0)
*/
export async function GET(_request: NextRequest, context: RouteContext): Promise<NextResponse> {
export async function GET(request: NextRequest, context: RouteContext): Promise<NextResponse> {
const { id: idOrUid, conversationId } = await context.params;
const { searchParams } = new URL(request.url);
const limit = Math.min(Number.parseInt(searchParams.get('limit') || '50', 10), 100);
const offset = Number.parseInt(searchParams.get('offset') || '0', 10);
try {
// Fetch agent by id or uid
@ -410,7 +418,7 @@ export async function GET(_request: NextRequest, context: RouteContext): Promise
return NextResponse.json({ success: false, error: 'Agent not found' }, { status: 404 });
}
// Fetch conversation with messages
// Fetch conversation
const conversation = await prisma.conversation.findUnique({
where: {
agentId_slug: {
@ -418,11 +426,6 @@ export async function GET(_request: NextRequest, context: RouteContext): Promise
slug: conversationId,
},
},
include: {
messages: {
orderBy: { createdAt: 'asc' },
},
},
});
if (!conversation) {
@ -432,7 +435,18 @@ export async function GET(_request: NextRequest, context: RouteContext): Promise
);
}
const mappedMessages = conversation.messages.map((m) => ({
// Fetch messages with pagination
const messages = await prisma.message.findMany({
where: { conversationId: conversation.id },
orderBy: { createdAt: 'asc' },
take: limit + 1,
skip: offset,
});
const hasMore = messages.length > limit;
const paginatedMessages = hasMore ? messages.slice(0, limit) : messages;
const mappedMessages = paginatedMessages.map((m) => ({
id: m.id,
role: m.role,
content: m.content,
@ -455,6 +469,11 @@ export async function GET(_request: NextRequest, context: RouteContext): Promise
updatedAt: conversation.updatedAt,
messages: mappedMessages,
},
pagination: {
limit,
offset,
hasMore,
},
});
} catch (error) {
console.error('Failed to fetch conversation:', error);

View file

@ -89,6 +89,157 @@ const CODE_TABS = [
{ id: 'aisdk', label: 'AI SDK' },
];
const FETCH_TABS = [
{ id: 'curl', label: 'cURL' },
{ id: 'typescript', label: 'TypeScript' },
{ id: 'python', label: 'Python' },
];
function ConversationFetchSection({ agent }: { agent: Agent }) {
const [activeTab, setActiveTab] = useState('curl');
const baseUrl = typeof window !== 'undefined' ? window.location.origin : 'https://tpmjs.com';
const listEndpoint = `${baseUrl}/api/agents/${agent.uid}/conversations`;
const getEndpoint = `${baseUrl}/api/agents/${agent.uid}/conversation/my-conv-1`;
const codeExamples: Record<string, { language: string; code: string }> = {
curl: {
language: 'bash',
code: `# List all conversations (with pagination)
curl '${listEndpoint}?limit=20&offset=0'
# Get a specific conversation with messages
curl '${getEndpoint}?limit=50&offset=0'
# Delete a conversation
curl -X DELETE '${getEndpoint}'`,
},
typescript: {
language: 'typescript',
code: `// List all conversations with pagination
const listConversations = async (limit = 20, offset = 0) => {
const response = await fetch(
\`${listEndpoint}?limit=\${limit}&offset=\${offset}\`
);
const data = await response.json();
// data.data: [{ id, slug, title, messageCount, createdAt, updatedAt }]
// data.pagination: { limit, offset, hasMore }
return data;
};
// Get conversation with paginated messages
const getConversation = async (
conversationId: string,
limit = 50,
offset = 0
) => {
const response = await fetch(
\`${baseUrl}/api/agents/${agent.uid}/conversation/\${conversationId}?limit=\${limit}&offset=\${offset}\`
);
const data = await response.json();
// data.data: { id, slug, title, messages: [...], createdAt, updatedAt }
// data.pagination: { limit, offset, hasMore }
return data;
};
// Fetch all messages (handling pagination)
const getAllMessages = async (conversationId: string) => {
const messages = [];
let offset = 0;
const limit = 50;
while (true) {
const { data, pagination } = await getConversation(
conversationId, limit, offset
);
messages.push(...data.messages);
if (!pagination.hasMore) break;
offset += limit;
}
return messages;
};`,
},
python: {
language: 'python',
code: `import requests
BASE_URL = '${baseUrl}/api/agents/${agent.uid}'
# List all conversations with pagination
def list_conversations(limit=20, offset=0):
response = requests.get(
f'{BASE_URL}/conversations',
params={'limit': limit, 'offset': offset}
)
return response.json()
# Get conversation with paginated messages
def get_conversation(conversation_id, limit=50, offset=0):
response = requests.get(
f'{BASE_URL}/conversation/{conversation_id}',
params={'limit': limit, 'offset': offset}
)
return response.json()
# Fetch all messages (handling pagination)
def get_all_messages(conversation_id):
messages = []
offset = 0
limit = 50
while True:
result = get_conversation(conversation_id, limit, offset)
messages.extend(result['data']['messages'])
if not result['pagination']['hasMore']:
break
offset += limit
return messages
# Example usage
conversations = list_conversations()
for conv in conversations['data']:
print(f"{conv['slug']}: {conv['title']} ({conv['messageCount']} messages)")`,
},
};
const currentExample = codeExamples[activeTab] ?? {
language: 'bash',
code: codeExamples.curl?.code ?? '',
};
return (
<div className="bg-background border border-border rounded-lg overflow-hidden mb-8">
<div className="flex items-center justify-between p-4 border-b border-border">
<h2 className="text-lg font-medium text-foreground">Fetch Conversations</h2>
<code className="text-xs text-foreground-secondary font-mono bg-surface px-2 py-1 rounded">
GET /api/agents/{agent.uid}/conversations
</code>
</div>
<div className="border-b border-border px-4 py-2">
<Tabs tabs={FETCH_TABS} activeTab={activeTab} onTabChange={setActiveTab} size="sm" />
</div>
<div className="p-4">
<CodeBlock language={currentExample.language} showCopy code={currentExample.code} />
</div>
<div className="px-4 pb-4">
<p className="text-xs text-foreground-tertiary">
Use <code className="bg-surface px-1 rounded">limit</code> and{' '}
<code className="bg-surface px-1 rounded">offset</code> query params for pagination. Check{' '}
<code className="bg-surface px-1 rounded">hasMore</code> to know if more results exist.
</p>
</div>
</div>
);
}
function ApiUsageSection({ agent, agentTools }: { agent: Agent; agentTools: AgentTool[] }) {
const [activeTab, setActiveTab] = useState('curl');
const baseUrl = typeof window !== 'undefined' ? window.location.origin : 'https://tpmjs.com';
@ -654,6 +805,9 @@ export default function AgentDetailPage(): React.ReactElement {
{/* API Usage */}
<ApiUsageSection agent={agent} agentTools={agentTools} />
{/* Fetch Conversations */}
<ConversationFetchSection agent={agent} />
{/* Tools Section */}
<div className="bg-background border border-border rounded-lg overflow-hidden mb-8">
<div className="flex items-center justify-between p-4 border-b border-border">