Ollama: Python SDK Integration
The Python SDK is the key that opens the door to local AI — three lines of code, seamless transition from script to application.
💡 Tip: The Python SDK (
ollama library) is essentially a wrapper around the Ollama REST API — the chat() method wraps /api/chat, generate() wraps /api/generate, and list() wraps /api/tags. Understanding this relationship helps with troubleshooting: when the SDK throws an error, you can use curl to call the API directly to determine if it's an SDK issue or an Ollama service issue.
📋 Prerequisites: You need to master the following first
- Lesson 5: REST API Fundamentals
1. What You Will Learn
- ollama Python library installation and synchronous/asynchronous APIs
chat()/generate()/list()usage- Asynchronous streaming response implementation for real-time output
- Python type annotations and error handling
- Alice's SupportBot V1 Python wrapper
2. A Real Story from a SaaS Founder
⚠️ Warning: When the Python SDK directly calls the Ollama API, Ollama has no built-in authentication. If your Ollama is bound to
0.0.0.0, SDK connection information (e.g., http://your-server:11434) may be exploited by others. In production, always add an authentication layer.
(1) The Pain Point: Shell Scripts Aren't Enough
Alice built a SupportBot prototype with curl, but Shell scripts are hard to maintain for conversation state, error handling, and integration with web services. She needs a proper programming language to build production-grade applications.
(2) The Solution: Python SDK Three-Line Integration
PYTHON
import ollama
response = ollama.chat(model='qwen2.5', messages=[
{'role': 'user', 'content': 'Hello'}
])
print(response['message']['content'])
3. Installation and API Overview
ℹ️ Info: The
ollama Python SDK defaults to connecting to http://localhost:11434, requiring no additional configuration. If Ollama is running at a different address, you can override it by setting the OLLAMA_HOST environment variable or specifying Client(host='http://...') in code.
💡 Tip: The Python SDK's
chat() method is recommended over generate() — chat supports multi-turn dialogue (messages array), while generate only supports single-turn. Even for single-shot questions, chat's role distinction (system/user/assistant) yields better output quality.
(1) Installation and Connection Verification
BASH
# Install the ollama Python package
pip install ollama
# Verify connection to Ollama server
python3 -c "import ollama; print(ollama.list())"
(2) Synchronous vs Asynchronous API Comparison
⚠️ Note: When using the asynchronous API (
AsyncClient), all calls must be prefixed with await, e.g., await client.chat(...). Forgetting await returns a coroutine object instead of the actual result — the program won't error but won't produce correct output. In async frameworks like FastAPI, you must use the asynchronous API; otherwise, it blocks the event loop and hurts concurrent performance.
| Dimension | Synchronous API | Asynchronous API |
|---|---|---|
| Module | ollama |
ollama (AsyncClient) |
| Call style | ollama.chat() |
await client.chat() |
| Blocking | Blocks current thread | Non-blocking, concurrent |
| Use case | Scripts, simple tools | Web services, concurrent processing |
| Streaming support | for chunk in stream |
async for chunk in stream |
▶ Example 1: Synchronous and Asynchronous Basic Calls
PYTHON
import ollama
import asyncio
# Synchronous call
def sync_chat():
response = ollama.chat(
model='qwen2.5',
messages=[{'role': 'user', 'content': 'Hello!'}]
)
print(response['message']['content'])
# Asynchronous call
async def async_chat():
client = ollama.AsyncClient()
response = await client.chat(
model='qwen2.5',
messages=[{'role': 'user', 'content': 'Hello!'}]
)
print(response['message']['content'])
sync_chat()
asyncio.run(async_chat())
Output:
TEXT
# Function defined successfully
4. Core Methods Deep Dive
(1) chat() Method
| Parameter | Type | Description |
|---|---|---|
model |
str | Model name |
messages |
list[dict] | Message list, each containing role/content |
stream |
bool | Whether to stream output |
format |
str | Output format: json |
options |
dict | Inference parameters (temperature, etc.) |
keep_alive |
str | Model memory retention time |
(2) generate() Method
| Parameter | Type | Description |
|---|---|---|
model |
str | Model name |
prompt |
str | Prompt text |
system |
str | System Prompt |
stream |
bool | Whether to stream |
options |
dict | Inference parameters |
▶ Example 2: chat vs generate Comparison
PYTHON
import ollama
# chat(): multi-turn with message history
response = ollama.chat(
model='qwen2.5',
messages=[
{'role': 'system', 'content': 'You are a SQL expert.'},
{'role': 'user', 'content': 'Write a query for top 5 customers'}
],
stream=False,
options={'temperature': 0.3}
)
print('chat:', response['message']['content'])
# generate(): single-shot text generation
response = ollama.generate(
model='qwen2.5',
prompt='Write a haiku about debugging',
system='You are a poet.',
stream=False
)
print('generate:', response['response'])
Output:
TEXT
chat:
generate:
5. Streaming Response Implementation
(1) Streaming Output Principle
sequenceDiagram
participant P as Python App
participant O as Ollama Server
P->>O: chat(stream=True)
loop Each token chunk
O-->>P: chunk {"content": "word"}
P->>P: print(word, end="")
end
O-->>P: chunk {"done": true}
▶ Example 3: Synchronous Streaming Output
PYTHON
import ollama
# Stream chat response in real-time
stream = ollama.chat(
model='qwen2.5',
messages=[{'role': 'user', 'content': 'Explain RAG in 3 sentences'}],
stream=True
)
for chunk in stream:
content = chunk['message']['content']
print(content, end='', flush=True)
print() # newline at end
Output:
TEXT
# Execution successful
▶ Example 4: Asynchronous Streaming Output
PYTHON
import ollama
import asyncio
async def stream_chat():
client = ollama.AsyncClient()
stream = await client.chat(
model='qwen2.5',
messages=[{'role': 'user', 'content': 'Tell me about Ollama'}],
stream=True
)
async for chunk in stream:
content = chunk['message']['content']
print(content, end='', flush=True)
print()
asyncio.run(stream_chat())
Output:
TEXT
# Function defined successfully
6. Error Handling and Type Annotations
(1) Common Error Types
| Error | Trigger Condition | Handling |
|---|---|---|
ConnectionError |
Ollama service not running | Start service or retry |
ResponseError |
Model not found / invalid parameters | Check Model name and parameters |
TimeoutError |
Inference timeout | Reduce num_ctx or increase timeout |
JSONDecodeError |
format=json output anomaly | Add JSON validation and retry |
▶ Example 5: Robust Error Handling
PYTHON
import ollama
import json
from typing import Optional
def safe_chat(
model: str,
messages: list[dict],
temperature: float = 0.3,
max_retries: int = 3
) -> Optional[str]:
"""Chat with error handling and retries."""
for attempt in range(max_retries):
try:
response = ollama.chat(
model=model,
messages=messages,
stream=False,
options={'temperature': temperature}
)
return response['message']['content']
except ConnectionError:
print(f"Connection failed (attempt {attempt + 1})")
if attempt == max_retries - 1:
return None
except ollama.ResponseError as e:
print(f"API error: {e.error}")
return None
except Exception as e:
print(f"Unexpected error: {e}")
if attempt == max_retries - 1:
return None
return None
# Usage
result = safe_chat('qwen2.5', [
{'role': 'user', 'content': 'What is your return policy?'}
])
if result:
print(result)
else:
print("Failed to get response")
Output:
TEXT
Failed to get response
7. Comprehensive Example: SupportBot V1 Python Wrapper
PYTHON
# ============================================
# Comprehensive: SupportBot V1
# Python wrapper for e-commerce customer service
# ============================================
import ollama
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class SupportBot:
model: str = "qwen2.5"
temperature: float = 0.4
max_history: int = 10
system_prompt: str = (
"You are SupportBot, an e-commerce customer service agent. "
"Be polite, concise, and helpful. "
"If unsure, say 'Let me connect you with a human agent.'"
)
messages: list[dict] = field(default_factory=list)
def __post_init__(self):
self.messages = [
{"role": "system", "content": self.system_prompt}
]
def chat(self, user_input: str) -> str:
self.messages.append({"role": "user", "content": user_input})
try:
response = ollama.chat(
model=self.model,
messages=self.messages[-self.max_history:],
stream=False,
options={"temperature": self.temperature}
)
assistant_msg = response["message"]["content"]
self.messages.append({"role": "assistant", "content": assistant_msg})
return assistant_msg
except Exception as e:
self.messages.pop() # Remove failed user message
return f"Error: {str(e)}"
def stream_chat(self, user_input: str):
self.messages.append({"role": "user", "content": user_input})
full_response = []
try:
stream = ollama.chat(
model=self.model,
messages=self.messages[-self.max_history:],
stream=True,
options={"temperature": self.temperature}
)
for chunk in stream:
content = chunk["message"]["content"]
full_response.append(content)
print(content, end="", flush=True)
print()
self.messages.append({"role": "assistant", "content": "".join(full_response)})
except Exception as e:
print(f"\nError: {e}")
def reset(self):
self.messages = [{"role": "system", "content": self.system_prompt}]
# Usage
if __name__ == "__main__":
bot = SupportBot(model="qwen2.5", temperature=0.4)
print("=== SupportBot V1 ===")
print(bot.chat("Where is my order #12345?"))
print()
print(bot.chat("It has been 7 days since I ordered."))
print()
print(bot.chat("Can I get a refund instead?"))
💻 Output:
TEXT
=== SupportBot V1 ===
I'd be happy to check on your order #12345. Based on our records, your order is currently in transit and expected to arrive within 2-3 business days. You can track it at our website.
I understand your concern. If you'd prefer a refund instead of waiting, I can initiate that for you. Our refund policy covers orders that haven't been delivered within the estimated timeframe.
Yes, I can process a full refund for order #12345. The refund will be credited to your original payment method within 3-5 business days. Would you like me to proceed?
❓ FAQ
Q pip install ollama fails, what should I do?
A Ensure Python >= 3.8 and pip is updated:
pip install --upgrade pip. For network issues, use a mirror: pip install ollama -i https://pypi.tuna.tsinghua.edu.cn/simple.Q How do I choose between synchronous and asynchronous APIs?
A Use synchronous for scripts and simple tools (simpler). Use asynchronous for web services (FastAPI/Django) to avoid blocking the event loop. The difference is minimal for single-user scenarios.
Q Streaming output doesn't display in Jupyter Notebook, what should I do?
A Jupyter has limited support for
flush=True. Use IPython.display.clear_output with loop updates, or switch to non-streaming mode.Q How do I specify the Ollama server address?
A Default connection is localhost:11434. Change via the
OLLAMA_HOST environment variable, or instantiate Client(host='http://...') in code.Q What happens when the messages list gets too long?
A Content exceeding the Model's context window (num_ctx) will be truncated. We recommend implementing a sliding window, keeping only the last N turns, or summarizing earlier conversations.
Q How do I get Inference speed and other statistics?
A Non-streaming responses contain
total_duration, eval_count, prompt_eval_count fields. The last chunk of streaming responses contains these statistics.📖 Summary
ollamaPython package — one-line install, both synchronous and asynchronous APIs availablechat()is suited for multi-turn dialogue;generate()is suited for single-shot generation- Streaming output is implemented via
stream=True+ iterating over chunks for real-time display - Async streaming uses
async foriteration, suited for web service concurrency - Error handling must cover ConnectionError, ResponseError, and TimeoutError
- SupportBot V1 uses a dataclass to encapsulate conversation history and Inference parameters
📝 Exercises
- Basic (Difficulty ⭐): Use the Python SDK to implement both
chat()andgenerate()once each, comparing output differences. - Intermediate (Difficulty ⭐⭐): Implement a streaming chat function with a sliding window (keeping the last 5 turns), automatically discarding the oldest messages when conversation exceeds 5 turns.
- Advanced (Difficulty ⭐⭐⭐): Build a SupportBot V1+ with error retry, timeout control, and JSON format output that saves customer service conversations to a file and logs Inference time for each call.