Skip to content

Commit

Permalink
fix Gemini, add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelcolvin committed Nov 4, 2024
1 parent 3c0f4d7 commit b539e62
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 56 deletions.
2 changes: 1 addition & 1 deletion pydantic_ai/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,4 @@ def cached_async_http_client() -> AsyncHTTPClient:
described in [encode/httpx#2026](https://github.com/encode/httpx/pull/2026), but when experimenting or showing
examples, it's very useful not to, this allows multiple Agents to use a single client.
"""
return AsyncHTTPClient()
return AsyncHTTPClient(timeout=30)
9 changes: 6 additions & 3 deletions pydantic_ai/models/gemini.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,14 +346,17 @@ class _GeminiResponse:

@dataclass
class _GeminiCandidates:
"""See <https://ai.google.dev/api/generate-content#v1beta.Candidate>."""

content: _GeminiContent
finish_reason: Annotated[Literal['STOP'], Field(alias='finishReason')]
"""
See https://ai.google.dev/api/generate-content#FinishReason, lots of other values are possible,
See <https://ai.google.dev/api/generate-content#FinishReason>, lots of other values are possible,
but let's wait until we see them and know what they mean to add them here.
"""
index: int
safety_ratings: Annotated[list[_GeminiSafetyRating], Field(alias='safetyRatings')]
avg_log_probs: Annotated[float, Field(alias='avgLogProbs')] | None = None
index: int | None = None
safety_ratings: Annotated[list[_GeminiSafetyRating], Field(alias='safetyRatings')] | None = None


@dataclass
Expand Down
20 changes: 20 additions & 0 deletions pydantic_ai_examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,23 @@ You can then ask the agent a question with:
```bash
(uv run/python) -m pydantic_ai_examples.rag search "How do I configure logfire to work with FastAPI?"
```

### `chat_app.py`

(Demonstrates: reusing chat history, serializing messages)

**TODO**: stream responses

Simple chat app example build with FastAPI.

This demonstrates storing chat history between requests and using it to give the model context for new responses.

Most of the complex logic here is in `chat_app.html` which includes the page layout and JavaScript to handle the chat.

Run the app with:

```bash
(uv run/python) -m pydantic_ai_examples.chat_app
```

Then open the app at [localhost:8000](http://localhost:8000).
70 changes: 18 additions & 52 deletions pydantic_ai_examples/chat_app.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,10 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chat App</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
main {
font-family: Verdana, sans-serif;
max-width: 600px;
margin: 40px auto;
border: 1px solid #ccc;
border-radius: 4px;
padding: 20px 30px;
}
input {
width: calc(100% - 22px);
padding: 10px;
margin-top: 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 1rem;
}
button {
margin: 10px 0 0 auto;
padding: 8px 15px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
display: block;
}
#conversation {
margin: 10px 5px;
}
#conversation div {
margin: 10px 0 0;
padding-top: 5px;
border-top: 1px solid #ccc;
}
#conversation .user::before {
content: 'You asked: ';
Expand All @@ -48,48 +19,40 @@
font-weight: bold;
display: block;
}
.center {
display: flex;
justify-content: center;
}
#spinner {
opacity: 0;
transition: opacity 500ms ease-in;
width: 30px;
height: 30px;
border: 3px solid #444;
border: 3px solid #222;
border-bottom-color: transparent;
border-radius: 50%;
display: inline-block;
box-sizing: border-box;
animation: rotation 1s linear infinite;
}
#spinner.active {
opacity: 1;
}
@keyframes rotation {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
#error {
display: none;
color: red;
#spinner.active {
opacity: 1;
}
</style>
</head>
<body>
<main id="main">
<main class="border rounded mx-auto my-5 p-4">
<h1>Chat App</h1>
<p>Ask me anything...</p>
<div id="conversation"></div>
<div class="center">
<div id="conversation" class="px-2"></div>
<div class="d-flex justify-content-center mb-3">
<div id="spinner"></div>
</div>
<form method="post">
<input id="prompt-input" name="prompt"/>
<button>Send</button>
<input id="prompt-input" name="prompt" class="form-control"/>
<div class="d-flex justify-content-end">
<button class="btn btn-primary mt-2">Send</button>
</div>
</form>
<div id="error">
<div id="error" class="d-none text-danger">
Error occurred, check the console for more information.
</div>
</main>
Expand All @@ -103,15 +66,16 @@ <h1>Chat App</h1>
const parent = document.getElementById('conversation');
for (const message of messages) {
let msgDiv = document.createElement('div');
msgDiv.classList.add(message.role);
msgDiv.classList.add('border-top', 'pt-2', message.role);
msgDiv.innerHTML = marked.parse(message.content);
parent.appendChild(msgDiv);
}
}

function onError(error) {
console.error(error);
document.getElementById('error').style.display = 'block';
document.getElementById('error').classList.remove('d-none');
document.getElementById('spinner').classList.remove('active');
}

async function fetchResponse(response) {
Expand Down Expand Up @@ -145,16 +109,18 @@ <h1>Chat App</h1>
e.preventDefault();
const spinner = document.getElementById('spinner');
spinner.classList.add('active');
const body = new FormData(e.target);

let input = document.getElementById('prompt-input')
input.value = '';
input.disabled = true;

const response = await fetch('/chat/', {method: 'POST', body: new FormData(e.target)});
const response = await fetch('/chat/', {method: 'POST', body});
await fetchResponse(response);
spinner.classList.remove('active');
}

// call onSubmit when form is submitted (e.g. user clicks the send button or hits Enter)
document.querySelector('form').addEventListener('submit', (e) => onSubmit(e).catch(onError));

// load messages on page load
Expand Down
7 changes: 7 additions & 0 deletions pydantic_ai_examples/chat_app.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
"""Simple chat app example build with FastAPI.
Run with:
uv run -m pydantic_ai_examples.chat_app
"""

from collections.abc import Iterator
from dataclasses import dataclass
from pathlib import Path
Expand Down

0 comments on commit b539e62

Please sign in to comment.