Skip to content

Commit 69ef1d1

Browse files
committed
Support automatic model configuration on agents
1 parent d2ebf6c commit 69ef1d1

File tree

17 files changed

+158
-312
lines changed

17 files changed

+158
-312
lines changed

docs/concepts/agents.mdx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ A more complex agent can be created by providing additional configuration. This
3131

3232
```python
3333
import controlflow as cf
34-
from langchain_openai import ChatOpenAI
3534

3635
agent = cf.Agent(
3736
name="Data Analyst",
@@ -41,7 +40,7 @@ agent = cf.Agent(
4140
"Browse the web for data and use Python to analyze it."
4241
),
4342
tools=[cf.tools.web.get_url, cf.tools.code.python],
44-
model=ChatpOpenAI('gpt-4o-mini'),
43+
model="openai/gpt-4o",
4544
interactive=True,
4645
)
4746
```

docs/examples/call-routing.mdx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ As you run this example, you'll see the conversation unfold in real-time, culmin
1717

1818
```python
1919
import random
20-
from enum import Enum
2120
import controlflow as cf
2221

2322
DEPARTMENTS = [

docs/examples/features/multi-llm.mdx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,12 @@ In this scenario, we'll create a workflow that analyzes customer feedback for a
1616

1717
```python
1818
import controlflow as cf
19-
from langchain_openai import ChatOpenAI
2019
from pydantic import BaseModel
2120
from typing import Literal
2221

23-
# Define our models
24-
gpt4_mini = ChatOpenAI(model="gpt-4o-mini")
25-
gpt4 = ChatOpenAI(model="gpt-4o")
26-
2722
# Create specialized agents
28-
classifier = cf.Agent(name="Classifier", model=gpt4_mini)
29-
summarizer = cf.Agent(name="Summarizer", model=gpt4)
23+
classifier = cf.Agent(name="Classifier", model="openai/gpt-4o-mini")
24+
summarizer = cf.Agent(name="Summarizer", model="openai/gpt-4o")
3025

3126
# Define our data models
3227
class Feedback(BaseModel):

docs/examples/headline-categorization.mdx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ The following code creates a function that classifies a given news headline into
1212

1313
```python
1414
import controlflow as cf
15-
from langchain_openai import ChatOpenAI
1615

17-
classifier = cf.Agent(model=ChatOpenAI(model="gpt-4o-mini"))
16+
classifier = cf.Agent(model="openai/gpt-4o-mini")
1817

1918
def classify_news(headline: str) -> str:
2019
return cf.run(
@@ -57,7 +56,7 @@ This implementation showcases several important ControlFlow features that enable
5756
1. **[Agents](/concepts/agents)**: We create an agent with a specific LLM model (GPT-4o mini) to perform the headline classification.
5857

5958
```python
60-
classifier = cf.Agent(model=ChatOpenAI(model="gpt-4o-mini"))
59+
classifier = cf.Agent(model="openai/gpt-4o-mini")
6160
```
6261

6362
2. **[Result types](/concepts/tasks/task-results)**: We use a list of strings as the `result_type` to constrain the output to one of the predefined categories. This ensures that the classification result is always one of the specified options.

docs/examples/named-entity-recognition.mdx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@ First, let's implement a function that extracts a simple list of entities:
1212

1313
```python
1414
import controlflow as cf
15-
from langchain_openai import ChatOpenAI
1615
from typing import List
1716

1817
extractor = cf.Agent(
1918
name="Named Entity Recognizer",
20-
model=ChatOpenAI(model="gpt-4o-mini"),
19+
model="openai/gpt-4o-mini",
2120
)
2221

2322
def extract_entities(text: str) -> List[str]:
@@ -86,7 +85,7 @@ This implementation showcases several important ControlFlow features that enable
8685
```python
8786
extractor = cf.Agent(
8887
name="Named Entity Recognizer",
89-
model=ChatOpenAI(model="gpt-4o-mini"),
88+
model="openai/gpt-4o-mini",
9089
)
9190
```
9291

docs/examples/sentiment-classifier.mdx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ The following code creates a function that classifies the sentiment of a given t
1313
```python
1414
import controlflow as cf
1515
from controlflow.tasks.validators import between
16-
from langchain_openai import ChatOpenAI
1716

18-
optimist = cf.Agent(model=ChatOpenAI(model="gpt-4o-mini"))
17+
optimist = cf.Agent(model="openai/gpt-4o-mini")
1918

2019
def sentiment(text: str) -> float:
2120
return cf.run(
@@ -58,7 +57,7 @@ This implementation showcases several important ControlFlow features that enable
5857
1. **[Agents](/concepts/agents)**: We create an agent with a specific LLM model (GPT-4o mini) to perform the sentiment analysis.
5958

6059
```python
61-
optimist = cf.Agent(model=ChatOpenAI(model="gpt-4o-mini"))
60+
optimist = cf.Agent(model="openai/gpt-4o-mini")
6261
```
6362

6463
3. **[Result types](/concepts/tasks/task-results)**: We specify `result_type=float` to ensure the sentiment score is returned as a float value.

docs/guides/llms.mdx

Lines changed: 48 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -19,38 +19,72 @@ Every ControlFlow agent can be assigned a specific LLM. When instantiating an ag
1919
ControlFlow agents can use any LangChain LLM class that supports chat-based APIs and tool calling. For a complete list of available models, settings, and instructions, please see LangChain's [LLM provider documentation](https://python.langchain.com/docs/integrations/chat/).
2020

2121
<Tip>
22-
ControlFlow includes OpenAI and Azure OpenAI models by default. To use other models, you'll need to first install the corresponding LangChain package and supply any required credentials. See the model's [documentation](https://python.langchain.com/docs/integrations/chat/) for more information.
22+
ControlFlow includes the required packages for OpenAI, Azure OpenAI, and Anthropic models by default. To use other models, you'll need to first install the corresponding LangChain package and supply any required credentials. See the model's [documentation](https://python.langchain.com/docs/integrations/chat/) for more information.
2323
</Tip>
2424

2525

26+
### Automatic configuration
27+
28+
ControlFlow can automatically load LLMs from certain providers, based on a parameter. The model parameter must have the form `{provider key}/{model name}`.
29+
30+
For example:
31+
```python
32+
import controlflow as cf
33+
34+
openai_agent = cf.Agent(model="openai/gpt-4o-mini")
35+
anthropic_agent = cf.Agent(model="anthropic/claude-3-haiku-20240307")
36+
groq_agent = cf.Agent(model="groq/mixtral-8x7b-32768")
37+
```
38+
39+
Note that loading a model from a string is convenient, but does not allow you to configure all of the model's parameters. For full control, see the docs on [manual configuration](#manual-configuration).
40+
41+
At this time, supported providers for automatic configuration include:
42+
43+
| Provider | Provider key | Required dependencies |
44+
| -------- | ----------------- | ----------------- |
45+
| OpenAI | `openai` | (included) |
46+
| Azure OpenAI | `azure-openai` | (included) |
47+
| Anthropic | `anthropic` | (included) |
48+
| Google | `google` | `langchain_google_genai` |
49+
| Groq | `groq` | `langchain_groq` |
50+
51+
If the required dependencies are not installed, ControlFlow will be unable to load the model and will raise an error.
52+
53+
54+
### Manual configuration
55+
56+
2657
To configure a different LLM, follow these steps:
2758
<Steps>
2859
<Step title="Install required packages">
29-
To use an LLM, first make sure you have installed the appropriate provider package. ControlFlow only includes `langchain_openai` by default. For example, to use an Anthropic model, first run:
30-
```
31-
pip install langchain_anthropic
60+
To use an LLM, first make sure you have installed the appropriate [provider package](https://python.langchain.com/docs/integrations/chat/). For example, to use a Google model, run:
61+
62+
```bash
63+
pip install langchain_google_genai
3264
```
3365
</Step>
3466
<Step title="Configure API keys">
35-
You must provide the correct API keys and configuration for the LLM you want to use. These can be provided as environment variables or when you create the model in your script. For example, to use an Anthropic model, set the `ANTHROPIC_API_KEY` environment variable:
67+
You must provide the correct API keys and configuration for the LLM you want to use. These can be provided as environment variables or when you create the model in your script. For example, to use an OpenAI model, you must set the `OPENAI_API_KEY` environment variable:
3668

69+
```bash
70+
export OPENAI_API_KEY=<your-api-key>
3771
```
38-
export ANTHROPIC_API_KEY=<your-api-key>
39-
```
40-
For model-specific instructions, please refer to the provider's documentation.
72+
For model-specific instructions, please refer to the provider's [documentation](https://python.langchain.com/docs/integrations/chat/).
4173
</Step>
74+
4275
<Step title="Create the model">
43-
Begin by creating the LLM object in your script. For example, to use Claude 3 Opus:
76+
Create the LLM model in your script, including any additional parameters. For example, to use Claude 3 Opus:
4477

4578
```python
4679
from langchain_anthropic import ChatAnthropic
4780

4881
# create the model
4982
model = ChatAnthropic(model='claude-3-opus-20240229')
5083
```
84+
5185
</Step>
5286
<Step title="Pass the model to an agent">
53-
Next, create an agent with the specified model:
87+
Finally, configure an agent with the model:
5488

5589
```python
5690
import controlflow as cf
@@ -59,40 +93,8 @@ import controlflow as cf
5993
agent = cf.Agent(model=model)
6094
```
6195
</Step>
62-
<Step title='Assign the agent to a task'>
63-
Finally, assign your agent to a task:
64-
65-
```python
66-
# assign the agent to a task
67-
task = cf.Task('Write a short poem about LLMs', agents=[agent])
68-
69-
# (optional) run the task
70-
task.run()
71-
```
72-
</Step>
7396
</Steps>
7497

75-
<Accordion title="Click here to copy the entire example script">
76-
77-
```python
78-
import controlflow as cf
79-
from langchain_anthropic import ChatAnthropic
80-
81-
# create the model
82-
model = ChatAnthropic(model='claude-3-opus-20240229')
83-
84-
# provide the model to an agent
85-
agent = cf.Agent(model=model)
86-
87-
# assign the agent to a task
88-
task = cf.Task('Write a short poem about LLMs', agents=[agent])
89-
90-
# (optional) run the task
91-
task.run()
92-
```
93-
</Accordion>
94-
95-
### Model configuration
9698

9799
In addition to choosing a specific model, you can also configure the model's parameters. For example, you can set the temperature for GPT-4o:
98100

@@ -133,33 +135,25 @@ assert cf.Agent('Marvin').model.model_name == 'claude-3-opus-20240229'
133135
```
134136
### From a string setting
135137

136-
You can also specify a default model using a string, which is convenient though it doesn't allow you to configure advanced model settings. The string must have the form `<provider>/<model name>`.
138+
You can also specify a default model using a string, which is convenient though it doesn't allow you to configure advanced model settings. This must be a string in the form `{provider key}/{model name}`, following the same guidelines as [automatic LLM configuration](#automatic-configuration).
137139

138140
You can apply this setting either by using an environment variable before you import ControlFlow or in your script at runtime. For example, to use GPT 3.5 Turbo as the default model:
139141

140142
<CodeGroup>
141143
```bash Set an environment variable
142-
export CONTROLFLOW_LLM_MODEL=openai/gpt-3.5-turbo
144+
export CONTROLFLOW_LLM_MODEL=openai/gpt-4o-mini
143145
```
144146

145147
```python Set a runtime variable
146148
import controlflow as cf
147149
# set the default model as a string
148-
cf.defaults.model = "openai/gpt-3.5-turbo"
150+
cf.defaults.model = "openai/gpt-4o-mini"
149151

150152
# check that the default model is loaded
151-
assert cf.Agent('Marvin').model.model_name == 'gpt-3.5-turbo'
153+
assert cf.Agent('Marvin').model.model_name == 'gpt-4o-mini'
152154
```
153155
</CodeGroup>
154156

155157
<Note>
156158
The default model can only be set by environment variable before importing ControlFlow. Once ControlFlow is imported, it reads the `controlflow.settings.llm_model` value to create the default model object.
157159
</Note>
158-
159-
160-
At this time, setting the default model via string is only supported for the following providers:
161-
- `openai`
162-
- `azure-openai`
163-
- `anthropic`
164-
- `google`
165-
- `groq`

docs/patterns/running-tasks.mdx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,9 @@ We can also use the `Moderated` strategy to have a more powerful model orchestra
207207

208208
```python Moderated
209209
import controlflow as cf
210-
from langchain_openai import ChatOpenAI
211210

212-
optimist = cf.Agent(name="Optimist", model=ChatOpenAI(model="gpt-4o-mini"))
213-
pessimist = cf.Agent(name="Pessimist", model=ChatOpenAI(model="gpt-4o-mini"))
211+
optimist = cf.Agent(name="Optimist", model="gpt-4o-mini")
212+
pessimist = cf.Agent(name="Pessimist", model="gpt-4o-mini")
214213
moderator = cf.Agent(name="Moderator")
215214

216215
cf.run(

docs/quickstart.mdx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,13 @@ This example uses an OpenAI model, but you can use any LangChain-compatible LLM
114114
</Warning>
115115

116116
<CodeGroup>
117-
```python Code
118-
from langchain_openai import ChatOpenAI
119-
from enum import Enum
117+
```python Codem
120118

121119

122120
# Create a specialized agent
123121
classifier = cf.Agent(
124122
name="Email Classifier",
125-
model=ChatOpenAI(model="gpt-4o-mini"),
123+
model="openai/gpt-4o-mini",
126124
instructions="You are an expert at quickly classifying emails.",
127125
)
128126

@@ -166,21 +164,19 @@ A flow provides a shared context and history for all agents, even across multipl
166164
<CodeGroup>
167165
```python Code
168166
import controlflow as cf
169-
from langchain_openai import ChatOpenAI
170-
from enum import Enum
171167

172168

173169
# Create agents
174170
classifier = cf.Agent(
175171
name="Email Classifier",
176-
model=ChatOpenAI(model="gpt-4o-mini"),
172+
model="openai/gpt-4o-mini",
177173
instructions="You are an expert at quickly classifying emails. Always "
178174
"respond with exactly one word: either 'important' or 'spam'."
179175
)
180176

181177
responder = cf.Agent(
182178
name="Email Responder",
183-
model=ChatOpenAI(model="gpt-4o"),
179+
model="openai/gpt-4o",
184180
instructions="You are an expert at crafting professional email responses. "
185181
"Your replies should be concise but friendly."
186182
)

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ tests = [
5252
"pytest-timeout",
5353
"pytest-xdist",
5454
"langchain_community",
55+
"langchain_google_genai",
56+
"langchain_groq",
5557
"duckduckgo-search",
5658
]
5759
dev = [

src/controlflow/agents/agent.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
AsyncGenerator,
1010
Generator,
1111
Optional,
12+
Union,
1213
)
1314

1415
from langchain_core.language_models import BaseChatModel
@@ -19,6 +20,7 @@
1920
from controlflow.events.base import Event
2021
from controlflow.instructions import get_instructions
2122
from controlflow.llm.messages import AIMessage, BaseMessage
23+
from controlflow.llm.models import get_model as get_model_from_string
2224
from controlflow.llm.rules import LLMRules
2325
from controlflow.tools.tools import (
2426
Tool,
@@ -78,9 +80,9 @@ class Agent(ControlFlowModel, abc.ABC):
7880

7981
# note: `model` should be typed as Optional[BaseChatModel] but V2 models can't have
8082
# V1 attributes without erroring, so we have to use Any.
81-
model: Optional[Any] = Field(
83+
model: Optional[Union[str, Any]] = Field(
8284
None,
83-
description="The LangChain BaseChatModel used by the agent. If not provided, the default model will be used.",
85+
description="The LangChain BaseChatModel used by the agent. If not provided, the default model will be used. A compatible string can be passed to automatically retrieve the model.",
8486
exclude=True,
8587
)
8688

@@ -130,6 +132,12 @@ def _generate_id(self):
130132
def _validate_tools(cls, tools: list[Tool]):
131133
return as_tools(tools or [])
132134

135+
@field_validator("model", mode="before")
136+
def _validate_model(cls, model: Optional[Union[str, Any]]):
137+
if isinstance(model, str):
138+
return get_model_from_string(model)
139+
return model
140+
133141
@field_serializer("tools")
134142
def _serialize_tools(self, tools: list[Tool]):
135143
tools = controlflow.tools.as_tools(tools)

src/controlflow/defaults.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from .agents import Agent
1212
from .events.history import History, InMemoryHistory
13-
from .llm.models import _get_initial_default_model, model_from_string
13+
from .llm.models import _get_initial_default_model, get_model
1414

1515
__all__ = ["defaults"]
1616

@@ -43,7 +43,7 @@ def __repr__(self) -> str:
4343
@field_validator("model")
4444
def _model(cls, v):
4545
if isinstance(v, str):
46-
v = model_from_string(v)
46+
v = get_model(v)
4747
elif v is not None and not isinstance(v, BaseChatModel):
4848
raise ValueError("Input must be an instance of BaseChatModel")
4949
return v

0 commit comments

Comments
 (0)