mirror of
https://github.com/crewAIInc/crewAI.git
synced 2025-12-16 12:28:30 +00:00
Compare commits
91 Commits
feat/ibm-m
...
knowledge
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0ad4576e2 | ||
|
|
6359b64d22 | ||
|
|
9329119f76 | ||
|
|
38c0d61b11 | ||
|
|
8564f5551f | ||
|
|
8a5404275f | ||
|
|
52189a46bc | ||
|
|
44ab749fda | ||
|
|
3c4504bd4f | ||
|
|
fde1ee45f9 | ||
|
|
6774bc2c53 | ||
|
|
94c62263ed | ||
|
|
495c3859af | ||
|
|
3e003f5e32 | ||
|
|
1c8b509d7d | ||
|
|
58af5c08f9 | ||
|
|
23276cbd76 | ||
|
|
fe18da5e11 | ||
|
|
76da972ce9 | ||
|
|
4663997b4c | ||
|
|
b185b9e289 | ||
|
|
787f2eaa7c | ||
|
|
e7d816fb2a | ||
|
|
8373c9b521 | ||
|
|
ec2fe6ff91 | ||
|
|
55e968c9e0 | ||
|
|
58bf2d57f7 | ||
|
|
705ee16c1c | ||
|
|
0c5b6f2a93 | ||
|
|
914067df37 | ||
|
|
de742c827d | ||
|
|
efa8a378a1 | ||
|
|
e882725b8a | ||
|
|
cbfdbe3b68 | ||
|
|
c8bf242633 | ||
|
|
70910dd7b4 | ||
|
|
b104404418 | ||
|
|
d579c5ae12 | ||
|
|
4831dcb85b | ||
|
|
cbfcde73ec | ||
|
|
b2c06d5b7a | ||
|
|
352d05370e | ||
|
|
0b9092702b | ||
|
|
8376698534 | ||
|
|
b90793874c | ||
|
|
cdf5233523 | ||
|
|
cb03ee60b8 | ||
|
|
10f445e18a | ||
|
|
3dc02310b6 | ||
|
|
98a708ca15 | ||
|
|
e70bc94ab6 | ||
|
|
9285ebf8a2 | ||
|
|
4ca785eb15 | ||
|
|
c57cbd8591 | ||
|
|
7fb1289205 | ||
|
|
f02681ae01 | ||
|
|
c725105b1f | ||
|
|
36aa4bcb46 | ||
|
|
b98f8f9fe1 | ||
|
|
bcfcf88e78 | ||
|
|
fd0de3a47e | ||
|
|
c7b9ae02fd | ||
|
|
4afb022572 | ||
|
|
8610faef22 | ||
|
|
6d677541c7 | ||
|
|
49220ec163 | ||
|
|
40a676b7ac | ||
|
|
50bf146d1e | ||
|
|
40d378abfb | ||
|
|
1b09b085a7 | ||
|
|
7b59c5b049 | ||
|
|
86ede8344c | ||
|
|
59165cbad8 | ||
|
|
4af263ca1e | ||
|
|
9f2acfe91f | ||
|
|
617ee989cd | ||
|
|
6131dbac4f | ||
|
|
1a35114c08 | ||
|
|
e856359e23 | ||
|
|
a8a2f80616 | ||
|
|
faa231e278 | ||
|
|
3d44795476 | ||
|
|
f50e709985 | ||
|
|
dc314c1151 | ||
|
|
75322b2de1 | ||
|
|
d70c542547 | ||
|
|
57201fb856 | ||
|
|
9b142e580b | ||
|
|
3878daffd6 | ||
|
|
34954e6f74 | ||
|
|
e66a135d5d |
2
.github/workflows/security-checker.yml
vendored
2
.github/workflows/security-checker.yml
vendored
@@ -19,5 +19,5 @@ jobs:
|
||||
run: pip install bandit
|
||||
|
||||
- name: Run Bandit
|
||||
run: bandit -c pyproject.toml -r src/ -lll
|
||||
run: bandit -c pyproject.toml -r src/ -ll
|
||||
|
||||
|
||||
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
run: uv python install 3.11.9
|
||||
|
||||
- name: Install the project
|
||||
run: uv sync --dev
|
||||
run: uv sync --dev --all-extras
|
||||
|
||||
- name: Run tests
|
||||
run: uv run pytest tests
|
||||
run: uv run pytest tests -vv
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -17,3 +17,7 @@ rc-tests/*
|
||||
temp/*
|
||||
.vscode/*
|
||||
crew_tasks_output.json
|
||||
.codesight
|
||||
.mypy_cache
|
||||
.ruff_cache
|
||||
.venv
|
||||
|
||||
@@ -22,7 +22,8 @@ A crew in crewAI represents a collaborative group of agents working together to
|
||||
| **Max RPM** _(optional)_ | `max_rpm` | Maximum requests per minute the crew adheres to during execution. Defaults to `None`. |
|
||||
| **Language** _(optional)_ | `language` | Language used for the crew, defaults to English. |
|
||||
| **Language File** _(optional)_ | `language_file` | Path to the language file to be used for the crew. |
|
||||
| **Memory** _(optional)_ | `memory` | Utilized for storing execution memories (short-term, long-term, entity memory). Defaults to `False`. |
|
||||
| **Memory** _(optional)_ | `memory` | Utilized for storing execution memories (short-term, long-term, entity memory). |
|
||||
| **Memory Config** _(optional)_ | `memory_config` | Configuration for the memory provider to be used by the crew. |
|
||||
| **Cache** _(optional)_ | `cache` | Specifies whether to use a cache for storing the results of tools' execution. Defaults to `True`. |
|
||||
| **Embedder** _(optional)_ | `embedder` | Configuration for the embedder to be used by the crew. Mostly used by memory for now. Default is `{"provider": "openai"}`. |
|
||||
| **Full Output** _(optional)_ | `full_output` | Whether the crew should return the full output with all tasks outputs or just the final output. Defaults to `False`. |
|
||||
|
||||
@@ -18,60 +18,63 @@ Flows allow you to create structured, event-driven workflows. They provide a sea
|
||||
|
||||
4. **Flexible Control Flow**: Implement conditional logic, loops, and branching within your workflows.
|
||||
|
||||
5. **Input Flexibility**: Flows can accept inputs to initialize or update their state, with different handling for structured and unstructured state management.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Let's create a simple Flow where you will use OpenAI to generate a random city in one task and then use that city to generate a fun fact in another task.
|
||||
|
||||
```python Code
|
||||
### Passing Inputs to Flows
|
||||
|
||||
Flows can accept inputs to initialize or update their state before execution. The way inputs are handled depends on whether the flow uses structured or unstructured state management.
|
||||
|
||||
#### Structured State Management
|
||||
|
||||
In structured state management, the flow's state is defined using a Pydantic `BaseModel`. Inputs must match the model's schema, and any updates will overwrite the default values.
|
||||
|
||||
```python
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
from dotenv import load_dotenv
|
||||
from litellm import completion
|
||||
from pydantic import BaseModel
|
||||
|
||||
class ExampleState(BaseModel):
|
||||
counter: int = 0
|
||||
message: str = ""
|
||||
|
||||
class ExampleFlow(Flow):
|
||||
model = "gpt-4o-mini"
|
||||
|
||||
class StructuredExampleFlow(Flow[ExampleState]):
|
||||
@start()
|
||||
def generate_city(self):
|
||||
print("Starting flow")
|
||||
def first_method(self):
|
||||
# Implementation
|
||||
|
||||
response = completion(
|
||||
model=self.model,
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Return the name of a random city in the world.",
|
||||
},
|
||||
],
|
||||
)
|
||||
flow = StructuredExampleFlow()
|
||||
flow.kickoff(inputs={"counter": 10})
|
||||
```
|
||||
|
||||
random_city = response["choices"][0]["message"]["content"]
|
||||
print(f"Random City: {random_city}")
|
||||
In this example, the `counter` is initialized to `10`, while `message` retains its default value.
|
||||
|
||||
return random_city
|
||||
#### Unstructured State Management
|
||||
|
||||
@listen(generate_city)
|
||||
def generate_fun_fact(self, random_city):
|
||||
response = completion(
|
||||
model=self.model,
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"Tell me a fun fact about {random_city}",
|
||||
},
|
||||
],
|
||||
)
|
||||
In unstructured state management, the flow's state is a dictionary. You can pass any dictionary to update the state.
|
||||
|
||||
fun_fact = response["choices"][0]["message"]["content"]
|
||||
return fun_fact
|
||||
```python
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
|
||||
class UnstructuredExampleFlow(Flow):
|
||||
@start()
|
||||
def first_method(self):
|
||||
# Implementation
|
||||
|
||||
flow = UnstructuredExampleFlow()
|
||||
flow.kickoff(inputs={"counter": 5, "message": "Initial message"})
|
||||
```
|
||||
|
||||
flow = ExampleFlow()
|
||||
result = flow.kickoff()
|
||||
Here, both `counter` and `message` are updated based on the provided inputs.
|
||||
|
||||
print(f"Generated fun fact: {result}")
|
||||
**Note:** Ensure that inputs for structured state management adhere to the defined schema to avoid validation errors.
|
||||
|
||||
### Example Flow
|
||||
|
||||
```python
|
||||
# Existing example code
|
||||
```
|
||||
|
||||
In the above example, we have created a simple Flow that generates a random city using OpenAI and then generates a fun fact about that city. The Flow consists of two tasks: `generate_city` and `generate_fun_fact`. The `generate_city` task is the starting point of the Flow, and the `generate_fun_fact` task listens for the output of the `generate_city` task.
|
||||
@@ -94,14 +97,14 @@ The `@listen()` decorator can be used in several ways:
|
||||
|
||||
1. **Listening to a Method by Name**: You can pass the name of the method you want to listen to as a string. When that method completes, the listener method will be triggered.
|
||||
|
||||
```python Code
|
||||
```python
|
||||
@listen("generate_city")
|
||||
def generate_fun_fact(self, random_city):
|
||||
# Implementation
|
||||
```
|
||||
|
||||
2. **Listening to a Method Directly**: You can pass the method itself. When that method completes, the listener method will be triggered.
|
||||
```python Code
|
||||
```python
|
||||
@listen(generate_city)
|
||||
def generate_fun_fact(self, random_city):
|
||||
# Implementation
|
||||
@@ -118,7 +121,7 @@ When you run a Flow, the final output is determined by the last method that comp
|
||||
Here's how you can access the final output:
|
||||
|
||||
<CodeGroup>
|
||||
```python Code
|
||||
```python
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
|
||||
class OutputExampleFlow(Flow):
|
||||
@@ -130,18 +133,17 @@ class OutputExampleFlow(Flow):
|
||||
def second_method(self, first_output):
|
||||
return f"Second method received: {first_output}"
|
||||
|
||||
|
||||
flow = OutputExampleFlow()
|
||||
final_output = flow.kickoff()
|
||||
|
||||
print("---- Final Output ----")
|
||||
print(final_output)
|
||||
````
|
||||
```
|
||||
|
||||
``` text Output
|
||||
```text
|
||||
---- Final Output ----
|
||||
Second method received: Output from first_method
|
||||
````
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
@@ -156,7 +158,7 @@ Here's an example of how to update and access the state:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Code
|
||||
```python
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -184,7 +186,7 @@ print("Final State:")
|
||||
print(flow.state)
|
||||
```
|
||||
|
||||
```text Output
|
||||
```text
|
||||
Final Output: Hello from first_method - updated by second_method
|
||||
Final State:
|
||||
counter=2 message='Hello from first_method - updated by second_method'
|
||||
@@ -208,10 +210,10 @@ allowing developers to choose the approach that best fits their application's ne
|
||||
In unstructured state management, all state is stored in the `state` attribute of the `Flow` class.
|
||||
This approach offers flexibility, enabling developers to add or modify state attributes on the fly without defining a strict schema.
|
||||
|
||||
```python Code
|
||||
```python
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
|
||||
class UntructuredExampleFlow(Flow):
|
||||
class UnstructuredExampleFlow(Flow):
|
||||
|
||||
@start()
|
||||
def first_method(self):
|
||||
@@ -230,8 +232,7 @@ class UntructuredExampleFlow(Flow):
|
||||
|
||||
print(f"State after third_method: {self.state}")
|
||||
|
||||
|
||||
flow = UntructuredExampleFlow()
|
||||
flow = UnstructuredExampleFlow()
|
||||
flow.kickoff()
|
||||
```
|
||||
|
||||
@@ -245,16 +246,14 @@ flow.kickoff()
|
||||
Structured state management leverages predefined schemas to ensure consistency and type safety across the workflow.
|
||||
By using models like Pydantic's `BaseModel`, developers can define the exact shape of the state, enabling better validation and auto-completion in development environments.
|
||||
|
||||
```python Code
|
||||
```python
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ExampleState(BaseModel):
|
||||
counter: int = 0
|
||||
message: str = ""
|
||||
|
||||
|
||||
class StructuredExampleFlow(Flow[ExampleState]):
|
||||
|
||||
@start()
|
||||
@@ -273,7 +272,6 @@ class StructuredExampleFlow(Flow[ExampleState]):
|
||||
|
||||
print(f"State after third_method: {self.state}")
|
||||
|
||||
|
||||
flow = StructuredExampleFlow()
|
||||
flow.kickoff()
|
||||
```
|
||||
@@ -307,7 +305,7 @@ The `or_` function in Flows allows you to listen to multiple methods and trigger
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Code
|
||||
```python
|
||||
from crewai.flow.flow import Flow, listen, or_, start
|
||||
|
||||
class OrExampleFlow(Flow):
|
||||
@@ -324,13 +322,11 @@ class OrExampleFlow(Flow):
|
||||
def logger(self, result):
|
||||
print(f"Logger: {result}")
|
||||
|
||||
|
||||
|
||||
flow = OrExampleFlow()
|
||||
flow.kickoff()
|
||||
```
|
||||
|
||||
```text Output
|
||||
```text
|
||||
Logger: Hello from the start method
|
||||
Logger: Hello from the second method
|
||||
```
|
||||
@@ -346,7 +342,7 @@ The `and_` function in Flows allows you to listen to multiple methods and trigge
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Code
|
||||
```python
|
||||
from crewai.flow.flow import Flow, and_, listen, start
|
||||
|
||||
class AndExampleFlow(Flow):
|
||||
@@ -368,7 +364,7 @@ flow = AndExampleFlow()
|
||||
flow.kickoff()
|
||||
```
|
||||
|
||||
```text Output
|
||||
```text
|
||||
---- Logger ----
|
||||
{'greeting': 'Hello from the start method', 'joke': 'What do computers eat? Microchips.'}
|
||||
```
|
||||
@@ -385,7 +381,7 @@ You can specify different routes based on the output of the method, allowing you
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Code
|
||||
```python
|
||||
import random
|
||||
from crewai.flow.flow import Flow, listen, router, start
|
||||
from pydantic import BaseModel
|
||||
@@ -416,12 +412,11 @@ class RouterFlow(Flow[ExampleState]):
|
||||
def fourth_method(self):
|
||||
print("Fourth method running")
|
||||
|
||||
|
||||
flow = RouterFlow()
|
||||
flow.kickoff()
|
||||
```
|
||||
|
||||
```text Output
|
||||
```text
|
||||
Starting the structured flow
|
||||
Third method running
|
||||
Fourth method running
|
||||
@@ -484,7 +479,7 @@ The `main.py` file is where you create your flow and connect the crews together.
|
||||
|
||||
Here's an example of how you can connect the `poem_crew` in the `main.py` file:
|
||||
|
||||
```python Code
|
||||
```python
|
||||
#!/usr/bin/env python
|
||||
from random import randint
|
||||
|
||||
@@ -577,18 +572,20 @@ This command will create a new directory for your crew within the `crews` folder
|
||||
|
||||
After adding a new crew, your folder structure will look like this:
|
||||
|
||||
name_of_flow/
|
||||
├── crews/
|
||||
│ ├── poem_crew/
|
||||
│ │ ├── config/
|
||||
│ │ │ ├── agents.yaml
|
||||
│ │ │ └── tasks.yaml
|
||||
│ │ └── poem_crew.py
|
||||
│ └── name_of_crew/
|
||||
│ ├── config/
|
||||
│ │ ├── agents.yaml
|
||||
│ │ └── tasks.yaml
|
||||
│ └── name_of_crew.py
|
||||
| Directory/File | Description |
|
||||
| :--------------------- | :----------------------------------------------------------------- |
|
||||
| `name_of_flow/` | Root directory for the flow. |
|
||||
| ├── `crews/` | Contains directories for specific crews. |
|
||||
| │ ├── `poem_crew/` | Directory for the "poem_crew" with its configurations and scripts. |
|
||||
| │ │ ├── `config/` | Configuration files directory for the "poem_crew". |
|
||||
| │ │ │ ├── `agents.yaml` | YAML file defining the agents for "poem_crew". |
|
||||
| │ │ │ └── `tasks.yaml` | YAML file defining the tasks for "poem_crew". |
|
||||
| │ │ └── `poem_crew.py` | Script for "poem_crew" functionality. |
|
||||
| └── `name_of_crew/` | Directory for the new crew. |
|
||||
| ├── `config/` | Configuration files directory for the new crew. |
|
||||
| │ ├── `agents.yaml` | YAML file defining the agents for the new crew. |
|
||||
| │ └── `tasks.yaml` | YAML file defining the tasks for the new crew. |
|
||||
| └── `name_of_crew.py` | Script for the new crew functionality. |
|
||||
|
||||
You can then customize the `agents.yaml` and `tasks.yaml` files to define the agents and tasks for your new crew. The `name_of_crew.py` file will contain the crew's logic, which you can modify to suit your needs.
|
||||
|
||||
@@ -610,7 +607,7 @@ CrewAI provides two convenient methods to generate plots of your flows:
|
||||
|
||||
If you are working directly with a flow instance, you can generate a plot by calling the `plot()` method on your flow object. This method will create an HTML file containing the interactive plot of your flow.
|
||||
|
||||
```python Code
|
||||
```python
|
||||
# Assuming you have a flow instance
|
||||
flow.plot("my_flow_plot")
|
||||
```
|
||||
|
||||
75
docs/concepts/knowledge.mdx
Normal file
75
docs/concepts/knowledge.mdx
Normal file
@@ -0,0 +1,75 @@
|
||||
---
|
||||
title: Knowledge
|
||||
description: What is knowledge in CrewAI and how to use it.
|
||||
icon: book
|
||||
---
|
||||
|
||||
# Using Knowledge in CrewAI
|
||||
|
||||
## Introduction
|
||||
|
||||
The Knowledge class in CrewAI provides a powerful way to manage and query knowledge sources for your AI agents. This guide will show you how to implement knowledge management in your CrewAI projects.
|
||||
Additionally, we have specific tools for generate knowledge sources for strings, text files, PDF's, and Spreadsheets. You can expand on any source type by extending the `KnowledgeSource` class.
|
||||
|
||||
## Basic Implementation
|
||||
|
||||
Here's a simple example of how to use the Knowledge class:
|
||||
|
||||
```python
|
||||
from crewai import Agent, Task, Crew, Process, LLM
|
||||
from crewai.knowledge.source.string_knowledge_source import StringKnowledgeSource
|
||||
|
||||
# Create a knowledge source
|
||||
content = "Users name is John. He is 30 years old and lives in San Francisco."
|
||||
string_source = StringKnowledgeSource(
|
||||
content=content, metadata={"preference": "personal"}
|
||||
)
|
||||
|
||||
|
||||
llm = LLM(model="gpt-4o-mini", temperature=0)
|
||||
# Create an agent with the knowledge store
|
||||
agent = Agent(
|
||||
role="About User",
|
||||
goal="You know everything about the user.",
|
||||
backstory="""You are a master at understanding people and their preferences.""",
|
||||
verbose=True,
|
||||
allow_delegation=False,
|
||||
llm=llm,
|
||||
)
|
||||
task = Task(
|
||||
description="Answer the following questions about the user: {question}",
|
||||
expected_output="An answer to the question.",
|
||||
agent=agent,
|
||||
)
|
||||
|
||||
crew = Crew(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
verbose=True,
|
||||
process=Process.sequential,
|
||||
knowledge={"sources": [string_source], "metadata": {"preference": "personal"}}, # Enable knowledge by adding the sources here. You can also add more sources to the sources list.
|
||||
)
|
||||
|
||||
result = crew.kickoff(inputs={"question": "What city does John live in and how old is he?"})
|
||||
```
|
||||
|
||||
|
||||
## Embedder Configuration
|
||||
|
||||
You can also configure the embedder for the knowledge store. This is useful if you want to use a different embedder for the knowledge store than the one used for the agents.
|
||||
|
||||
```python
|
||||
...
|
||||
string_source = StringKnowledgeSource(
|
||||
content="Users name is John. He is 30 years old and lives in San Francisco.",
|
||||
metadata={"preference": "personal"}
|
||||
)
|
||||
crew = Crew(
|
||||
...
|
||||
knowledge={
|
||||
"sources": [string_source],
|
||||
"metadata": {"preference": "personal"},
|
||||
"embedder_config": {"provider": "openai", "config": {"model": "text-embedding-3-small"}},
|
||||
},
|
||||
)
|
||||
```
|
||||
@@ -25,7 +25,102 @@ By default, CrewAI uses the `gpt-4o-mini` model. It uses environment variables i
|
||||
- `OPENAI_API_BASE`
|
||||
- `OPENAI_API_KEY`
|
||||
|
||||
### 2. Custom LLM Objects
|
||||
### 2. Updating YAML files
|
||||
|
||||
You can update the `agents.yml` file to refer to the LLM you want to use:
|
||||
|
||||
```yaml Code
|
||||
researcher:
|
||||
role: Research Specialist
|
||||
goal: Conduct comprehensive research and analysis to gather relevant information,
|
||||
synthesize findings, and produce well-documented insights.
|
||||
backstory: A dedicated research professional with years of experience in academic
|
||||
investigation, literature review, and data analysis, known for thorough and
|
||||
methodical approaches to complex research questions.
|
||||
verbose: true
|
||||
llm: openai/gpt-4o
|
||||
# llm: azure/gpt-4o-mini
|
||||
# llm: gemini/gemini-pro
|
||||
# llm: anthropic/claude-3-5-sonnet-20240620
|
||||
# llm: bedrock/anthropic.claude-3-sonnet-20240229-v1:0
|
||||
# llm: mistral/mistral-large-latest
|
||||
# llm: ollama/llama3:70b
|
||||
# llm: groq/llama-3.2-90b-vision-preview
|
||||
# llm: watsonx/meta-llama/llama-3-1-70b-instruct
|
||||
# llm: nvidia_nim/meta/llama3-70b-instruct
|
||||
# llm: sambanova/Meta-Llama-3.1-8B-Instruct
|
||||
# ...
|
||||
```
|
||||
|
||||
Keep in mind that you will need to set certain ENV vars depending on the model you are
|
||||
using to account for the credentials or set a custom LLM object like described below.
|
||||
Here are some of the required ENV vars for some of the LLM integrations:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="OpenAI">
|
||||
```python Code
|
||||
OPENAI_API_KEY=<your-api-key>
|
||||
OPENAI_API_BASE=<optional-custom-base-url>
|
||||
OPENAI_MODEL_NAME=<openai-model-name>
|
||||
OPENAI_ORGANIZATION=<your-org-id> # OPTIONAL
|
||||
OPENAI_API_BASE=<openaiai-api-base> # OPTIONAL
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Anthropic">
|
||||
```python Code
|
||||
ANTHROPIC_API_KEY=<your-api-key>
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Google">
|
||||
```python Code
|
||||
GEMINI_API_KEY=<your-api-key>
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Azure">
|
||||
```python Code
|
||||
AZURE_API_KEY=<your-api-key> # "my-azure-api-key"
|
||||
AZURE_API_BASE=<your-resource-url> # "https://example-endpoint.openai.azure.com"
|
||||
AZURE_API_VERSION=<api-version> # "2023-05-15"
|
||||
AZURE_AD_TOKEN=<your-azure-ad-token> # Optional
|
||||
AZURE_API_TYPE=<your-azure-api-type> # Optional
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="AWS Bedrock">
|
||||
```python Code
|
||||
AWS_ACCESS_KEY_ID=<your-access-key>
|
||||
AWS_SECRET_ACCESS_KEY=<your-secret-key>
|
||||
AWS_DEFAULT_REGION=<your-region>
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Mistral">
|
||||
```python Code
|
||||
MISTRAL_API_KEY=<your-api-key>
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Groq">
|
||||
```python Code
|
||||
GROQ_API_KEY=<your-api-key>
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="IBM watsonx.ai">
|
||||
```python Code
|
||||
WATSONX_URL=<your-url> # (required) Base URL of your WatsonX instance
|
||||
WATSONX_APIKEY=<your-apikey> # (required) IBM cloud API key
|
||||
WATSONX_TOKEN=<your-token> # (required) IAM auth token (alternative to APIKEY)
|
||||
WATSONX_PROJECT_ID=<your-project-id> # (optional) Project ID of your WatsonX instance
|
||||
WATSONX_DEPLOYMENT_SPACE_ID=<your-space-id> # (optional) ID of deployment space for deployed models
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### 3. Custom LLM Objects
|
||||
|
||||
Pass a custom LLM implementation or object from another library.
|
||||
|
||||
@@ -102,7 +197,7 @@ When configuring an LLM for your agent, you have access to a wide range of param
|
||||
|
||||
These are examples of how to configure LLMs for your agent.
|
||||
|
||||
<AccordionGroup>
|
||||
<AccordionGroup>
|
||||
<Accordion title="OpenAI">
|
||||
|
||||
```python Code
|
||||
@@ -131,13 +226,12 @@ These are examples of how to configure LLMs for your agent.
|
||||
|
||||
llm = LLM(
|
||||
model="cerebras/llama-3.1-70b",
|
||||
base_url="https://api.cerebras.ai/v1",
|
||||
api_key="your-api-key-here"
|
||||
)
|
||||
agent = Agent(llm=llm, ...)
|
||||
agent = Agent(llm=llm, ...)
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
|
||||
<Accordion title="Ollama (Local LLMs)">
|
||||
|
||||
CrewAI supports using Ollama for running open-source models locally:
|
||||
@@ -151,7 +245,7 @@ These are examples of how to configure LLMs for your agent.
|
||||
|
||||
agent = Agent(
|
||||
llm=LLM(
|
||||
model="ollama/llama3.1",
|
||||
model="ollama/llama3.1",
|
||||
base_url="http://localhost:11434"
|
||||
),
|
||||
...
|
||||
@@ -165,8 +259,7 @@ These are examples of how to configure LLMs for your agent.
|
||||
from crewai import LLM
|
||||
|
||||
llm = LLM(
|
||||
model="groq/llama3-8b-8192",
|
||||
base_url="https://api.groq.com/openai/v1",
|
||||
model="groq/llama3-8b-8192",
|
||||
api_key="your-api-key-here"
|
||||
)
|
||||
agent = Agent(llm=llm, ...)
|
||||
@@ -180,21 +273,18 @@ These are examples of how to configure LLMs for your agent.
|
||||
|
||||
llm = LLM(
|
||||
model="anthropic/claude-3-5-sonnet-20241022",
|
||||
base_url="https://api.anthropic.com/v1",
|
||||
api_key="your-api-key-here"
|
||||
)
|
||||
agent = Agent(llm=llm, ...)
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Fireworks">
|
||||
|
||||
<Accordion title="Fireworks AI">
|
||||
```python Code
|
||||
from crewai import LLM
|
||||
|
||||
llm = LLM(
|
||||
model="fireworks/meta-llama-3.1-8b-instruct",
|
||||
base_url="https://api.fireworks.ai/inference/v1",
|
||||
model="fireworks_ai/accounts/fireworks/models/llama-v3-70b-instruct",
|
||||
api_key="your-api-key-here"
|
||||
)
|
||||
agent = Agent(llm=llm, ...)
|
||||
@@ -207,8 +297,7 @@ These are examples of how to configure LLMs for your agent.
|
||||
from crewai import LLM
|
||||
|
||||
llm = LLM(
|
||||
model="gemini/gemini-1.5-flash",
|
||||
base_url="https://api.gemini.google.com/v1",
|
||||
model="gemini/gemini-1.5-pro-002",
|
||||
api_key="your-api-key-here"
|
||||
)
|
||||
agent = Agent(llm=llm, ...)
|
||||
@@ -230,6 +319,29 @@ These are examples of how to configure LLMs for your agent.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="IBM watsonx.ai">
|
||||
You can use IBM Watson by seeting the following ENV vars:
|
||||
|
||||
```python Code
|
||||
WATSONX_URL=<your-url>
|
||||
WATSONX_APIKEY=<your-apikey>
|
||||
WATSONX_PROJECT_ID=<your-project-id>
|
||||
```
|
||||
|
||||
You can then define your agents llms by updating the `agents.yml`
|
||||
|
||||
```yaml Code
|
||||
researcher:
|
||||
role: Research Specialist
|
||||
goal: Conduct comprehensive research and analysis to gather relevant information,
|
||||
synthesize findings, and produce well-documented insights.
|
||||
backstory: A dedicated research professional with years of experience in academic
|
||||
investigation, literature review, and data analysis, known for thorough and
|
||||
methodical approaches to complex research questions.
|
||||
verbose: true
|
||||
llm: watsonx/meta-llama/llama-3-1-70b-instruct
|
||||
```
|
||||
|
||||
You can also set up agents more dynamically as a base level LLM instance, like bellow:
|
||||
|
||||
```python Code
|
||||
from crewai import LLM
|
||||
@@ -242,6 +354,20 @@ These are examples of how to configure LLMs for your agent.
|
||||
agent = Agent(llm=llm, ...)
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Hugging Face">
|
||||
|
||||
```python Code
|
||||
from crewai import LLM
|
||||
|
||||
llm = LLM(
|
||||
model="huggingface/meta-llama/Meta-Llama-3.1-8B-Instruct",
|
||||
api_key="your-api-key-here",
|
||||
base_url="your_api_endpoint"
|
||||
)
|
||||
agent = Agent(llm=llm, ...)
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Changing the Base API URL
|
||||
|
||||
@@ -18,6 +18,7 @@ reason, and learn from past interactions.
|
||||
| **Long-Term Memory** | Preserves valuable insights and learnings from past executions, allowing agents to build and refine their knowledge over time. |
|
||||
| **Entity Memory** | Captures and organizes information about entities (people, places, concepts) encountered during tasks, facilitating deeper understanding and relationship mapping. Uses `RAG` for storing entity information. |
|
||||
| **Contextual Memory**| Maintains the context of interactions by combining `ShortTermMemory`, `LongTermMemory`, and `EntityMemory`, aiding in the coherence and relevance of agent responses over a sequence of tasks or a conversation. |
|
||||
| **User Memory** | Stores user-specific information and preferences, enhancing personalization and user experience. |
|
||||
|
||||
## How Memory Systems Empower Agents
|
||||
|
||||
@@ -92,6 +93,47 @@ my_crew = Crew(
|
||||
)
|
||||
```
|
||||
|
||||
## Integrating Mem0 for Enhanced User Memory
|
||||
|
||||
[Mem0](https://mem0.ai/) is a self-improving memory layer for LLM applications, enabling personalized AI experiences.
|
||||
|
||||
To include user-specific memory you can get your API key [here](https://app.mem0.ai/dashboard/api-keys) and refer the [docs](https://docs.mem0.ai/platform/quickstart#4-1-create-memories) for adding user preferences.
|
||||
|
||||
|
||||
```python Code
|
||||
import os
|
||||
from crewai import Crew, Process
|
||||
from mem0 import MemoryClient
|
||||
|
||||
# Set environment variables for Mem0
|
||||
os.environ["MEM0_API_KEY"] = "m0-xx"
|
||||
|
||||
# Step 1: Record preferences based on past conversation or user input
|
||||
client = MemoryClient()
|
||||
messages = [
|
||||
{"role": "user", "content": "Hi there! I'm planning a vacation and could use some advice."},
|
||||
{"role": "assistant", "content": "Hello! I'd be happy to help with your vacation planning. What kind of destination do you prefer?"},
|
||||
{"role": "user", "content": "I am more of a beach person than a mountain person."},
|
||||
{"role": "assistant", "content": "That's interesting. Do you like hotels or Airbnb?"},
|
||||
{"role": "user", "content": "I like Airbnb more."},
|
||||
]
|
||||
client.add(messages, user_id="john")
|
||||
|
||||
# Step 2: Create a Crew with User Memory
|
||||
|
||||
crew = Crew(
|
||||
agents=[...],
|
||||
tasks=[...],
|
||||
verbose=True,
|
||||
process=Process.sequential,
|
||||
memory=True,
|
||||
memory_config={
|
||||
"provider": "mem0",
|
||||
"config": {"user_id": "john"},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
## Additional Embedding Providers
|
||||
|
||||
@@ -254,6 +296,31 @@ my_crew = Crew(
|
||||
)
|
||||
```
|
||||
|
||||
### Using Watson embeddings
|
||||
|
||||
```python Code
|
||||
from crewai import Crew, Agent, Task, Process
|
||||
|
||||
# Note: Ensure you have installed and imported `ibm_watsonx_ai` for Watson embeddings to work.
|
||||
|
||||
my_crew = Crew(
|
||||
agents=[...],
|
||||
tasks=[...],
|
||||
process=Process.sequential,
|
||||
memory=True,
|
||||
verbose=True,
|
||||
embedder={
|
||||
"provider": "watson",
|
||||
"config": {
|
||||
"model": "<model_name>",
|
||||
"api_url": "<api_url>",
|
||||
"api_key": "<YOUR_API_KEY>",
|
||||
"project_id": "<YOUR_PROJECT_ID>",
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### Resetting Memory
|
||||
|
||||
```shell
|
||||
|
||||
@@ -5,13 +5,14 @@ icon: screwdriver-wrench
|
||||
---
|
||||
|
||||
## Introduction
|
||||
CrewAI tools empower agents with capabilities ranging from web searching and data analysis to collaboration and delegating tasks among coworkers.
|
||||
|
||||
CrewAI tools empower agents with capabilities ranging from web searching and data analysis to collaboration and delegating tasks among coworkers.
|
||||
This documentation outlines how to create, integrate, and leverage these tools within the CrewAI framework, including a new focus on collaboration tools.
|
||||
|
||||
## What is a Tool?
|
||||
|
||||
A tool in CrewAI is a skill or function that agents can utilize to perform various actions.
|
||||
This includes tools from the [CrewAI Toolkit](https://github.com/joaomdmoura/crewai-tools) and [LangChain Tools](https://python.langchain.com/docs/integrations/tools),
|
||||
A tool in CrewAI is a skill or function that agents can utilize to perform various actions.
|
||||
This includes tools from the [CrewAI Toolkit](https://github.com/joaomdmoura/crewai-tools) and [LangChain Tools](https://python.langchain.com/docs/integrations/tools),
|
||||
enabling everything from simple searches to complex interactions and effective teamwork among agents.
|
||||
|
||||
## Key Characteristics of Tools
|
||||
@@ -103,57 +104,53 @@ crew.kickoff()
|
||||
|
||||
Here is a list of the available tools and their descriptions:
|
||||
|
||||
| Tool | Description |
|
||||
| :-------------------------- | :-------------------------------------------------------------------------------------------- |
|
||||
| **BrowserbaseLoadTool** | A tool for interacting with and extracting data from web browsers. |
|
||||
| **CodeDocsSearchTool** | A RAG tool optimized for searching through code documentation and related technical documents. |
|
||||
| **CodeInterpreterTool** | A tool for interpreting python code. |
|
||||
| **ComposioTool** | Enables use of Composio tools. |
|
||||
| **CSVSearchTool** | A RAG tool designed for searching within CSV files, tailored to handle structured data. |
|
||||
| **DALL-E Tool** | A tool for generating images using the DALL-E API. |
|
||||
| **DirectorySearchTool** | A RAG tool for searching within directories, useful for navigating through file systems. |
|
||||
| **DOCXSearchTool** | A RAG tool aimed at searching within DOCX documents, ideal for processing Word files. |
|
||||
| **DirectoryReadTool** | Facilitates reading and processing of directory structures and their contents. |
|
||||
| **EXASearchTool** | A tool designed for performing exhaustive searches across various data sources. |
|
||||
| **FileReadTool** | Enables reading and extracting data from files, supporting various file formats. |
|
||||
| **FirecrawlSearchTool** | A tool to search webpages using Firecrawl and return the results. |
|
||||
| **FirecrawlCrawlWebsiteTool** | A tool for crawling webpages using Firecrawl. |
|
||||
| **FirecrawlScrapeWebsiteTool** | A tool for scraping webpages URL using Firecrawl and returning its contents. |
|
||||
| **GithubSearchTool** | A RAG tool for searching within GitHub repositories, useful for code and documentation search.|
|
||||
| **SerperDevTool** | A specialized tool for development purposes, with specific functionalities under development. |
|
||||
| **TXTSearchTool** | A RAG tool focused on searching within text (.txt) files, suitable for unstructured data. |
|
||||
| **JSONSearchTool** | A RAG tool designed for searching within JSON files, catering to structured data handling. |
|
||||
| **LlamaIndexTool** | Enables the use of LlamaIndex tools. |
|
||||
| **MDXSearchTool** | A RAG tool tailored for searching within Markdown (MDX) files, useful for documentation. |
|
||||
| **PDFSearchTool** | A RAG tool aimed at searching within PDF documents, ideal for processing scanned documents. |
|
||||
| **PGSearchTool** | A RAG tool optimized for searching within PostgreSQL databases, suitable for database queries. |
|
||||
| **Vision Tool** | A tool for generating images using the DALL-E API. |
|
||||
| **RagTool** | A general-purpose RAG tool capable of handling various data sources and types. |
|
||||
| **ScrapeElementFromWebsiteTool** | Enables scraping specific elements from websites, useful for targeted data extraction. |
|
||||
| **ScrapeWebsiteTool** | Facilitates scraping entire websites, ideal for comprehensive data collection. |
|
||||
| **WebsiteSearchTool** | A RAG tool for searching website content, optimized for web data extraction. |
|
||||
| **XMLSearchTool** | A RAG tool designed for searching within XML files, suitable for structured data formats. |
|
||||
| **YoutubeChannelSearchTool**| A RAG tool for searching within YouTube channels, useful for video content analysis. |
|
||||
| **YoutubeVideoSearchTool** | A RAG tool aimed at searching within YouTube videos, ideal for video data extraction. |
|
||||
| Tool | Description |
|
||||
| :------------------------------- | :--------------------------------------------------------------------------------------------- |
|
||||
| **BrowserbaseLoadTool** | A tool for interacting with and extracting data from web browsers. |
|
||||
| **CodeDocsSearchTool** | A RAG tool optimized for searching through code documentation and related technical documents. |
|
||||
| **CodeInterpreterTool** | A tool for interpreting python code. |
|
||||
| **ComposioTool** | Enables use of Composio tools. |
|
||||
| **CSVSearchTool** | A RAG tool designed for searching within CSV files, tailored to handle structured data. |
|
||||
| **DALL-E Tool** | A tool for generating images using the DALL-E API. |
|
||||
| **DirectorySearchTool** | A RAG tool for searching within directories, useful for navigating through file systems. |
|
||||
| **DOCXSearchTool** | A RAG tool aimed at searching within DOCX documents, ideal for processing Word files. |
|
||||
| **DirectoryReadTool** | Facilitates reading and processing of directory structures and their contents. |
|
||||
| **EXASearchTool** | A tool designed for performing exhaustive searches across various data sources. |
|
||||
| **FileReadTool** | Enables reading and extracting data from files, supporting various file formats. |
|
||||
| **FirecrawlSearchTool** | A tool to search webpages using Firecrawl and return the results. |
|
||||
| **FirecrawlCrawlWebsiteTool** | A tool for crawling webpages using Firecrawl. |
|
||||
| **FirecrawlScrapeWebsiteTool** | A tool for scraping webpages URL using Firecrawl and returning its contents. |
|
||||
| **GithubSearchTool** | A RAG tool for searching within GitHub repositories, useful for code and documentation search. |
|
||||
| **SerperDevTool** | A specialized tool for development purposes, with specific functionalities under development. |
|
||||
| **TXTSearchTool** | A RAG tool focused on searching within text (.txt) files, suitable for unstructured data. |
|
||||
| **JSONSearchTool** | A RAG tool designed for searching within JSON files, catering to structured data handling. |
|
||||
| **LlamaIndexTool** | Enables the use of LlamaIndex tools. |
|
||||
| **MDXSearchTool** | A RAG tool tailored for searching within Markdown (MDX) files, useful for documentation. |
|
||||
| **PDFSearchTool** | A RAG tool aimed at searching within PDF documents, ideal for processing scanned documents. |
|
||||
| **PGSearchTool** | A RAG tool optimized for searching within PostgreSQL databases, suitable for database queries. |
|
||||
| **Vision Tool** | A tool for generating images using the DALL-E API. |
|
||||
| **RagTool** | A general-purpose RAG tool capable of handling various data sources and types. |
|
||||
| **ScrapeElementFromWebsiteTool** | Enables scraping specific elements from websites, useful for targeted data extraction. |
|
||||
| **ScrapeWebsiteTool** | Facilitates scraping entire websites, ideal for comprehensive data collection. |
|
||||
| **WebsiteSearchTool** | A RAG tool for searching website content, optimized for web data extraction. |
|
||||
| **XMLSearchTool** | A RAG tool designed for searching within XML files, suitable for structured data formats. |
|
||||
| **YoutubeChannelSearchTool** | A RAG tool for searching within YouTube channels, useful for video content analysis. |
|
||||
| **YoutubeVideoSearchTool** | A RAG tool aimed at searching within YouTube videos, ideal for video data extraction. |
|
||||
|
||||
## Creating your own Tools
|
||||
|
||||
<Tip>
|
||||
Developers can craft `custom tools` tailored for their agent’s needs or utilize pre-built options.
|
||||
Developers can craft `custom tools` tailored for their agent’s needs or
|
||||
utilize pre-built options.
|
||||
</Tip>
|
||||
|
||||
To create your own CrewAI tools you will need to install our extra tools package:
|
||||
|
||||
```bash
|
||||
pip install 'crewai[tools]'
|
||||
```
|
||||
|
||||
Once you do that there are two main ways for one to create a CrewAI tool:
|
||||
There are two main ways for one to create a CrewAI tool:
|
||||
|
||||
### Subclassing `BaseTool`
|
||||
|
||||
```python Code
|
||||
from crewai_tools import BaseTool
|
||||
from crewai.tools import BaseTool
|
||||
|
||||
|
||||
class MyCustomTool(BaseTool):
|
||||
name: str = "Name of my tool"
|
||||
@@ -167,7 +164,7 @@ class MyCustomTool(BaseTool):
|
||||
### Utilizing the `tool` Decorator
|
||||
|
||||
```python Code
|
||||
from crewai_tools import tool
|
||||
from crewai.tools import tool
|
||||
@tool("Name of my tool")
|
||||
def my_tool(question: str) -> str:
|
||||
"""Clear description for what this tool is useful for, your agent will need this information to use it."""
|
||||
@@ -178,11 +175,13 @@ def my_tool(question: str) -> str:
|
||||
### Custom Caching Mechanism
|
||||
|
||||
<Tip>
|
||||
Tools can optionally implement a `cache_function` to fine-tune caching behavior. This function determines when to cache results based on specific conditions, offering granular control over caching logic.
|
||||
Tools can optionally implement a `cache_function` to fine-tune caching
|
||||
behavior. This function determines when to cache results based on specific
|
||||
conditions, offering granular control over caching logic.
|
||||
</Tip>
|
||||
|
||||
```python Code
|
||||
from crewai_tools import tool
|
||||
from crewai.tools import tool
|
||||
|
||||
@tool
|
||||
def multiplication_tool(first_number: int, second_number: int) -> str:
|
||||
@@ -208,6 +207,6 @@ writer1 = Agent(
|
||||
|
||||
## Conclusion
|
||||
|
||||
Tools are pivotal in extending the capabilities of CrewAI agents, enabling them to undertake a broad spectrum of tasks and collaborate effectively.
|
||||
When building solutions with CrewAI, leverage both custom and existing tools to empower your agents and enhance the AI ecosystem. Consider utilizing error handling,
|
||||
caching mechanisms, and the flexibility of tool arguments to optimize your agents' performance and capabilities.
|
||||
Tools are pivotal in extending the capabilities of CrewAI agents, enabling them to undertake a broad spectrum of tasks and collaborate effectively.
|
||||
When building solutions with CrewAI, leverage both custom and existing tools to empower your agents and enhance the AI ecosystem. Consider utilizing error handling,
|
||||
caching mechanisms, and the flexibility of tool arguments to optimize your agents' performance and capabilities.
|
||||
|
||||
@@ -6,25 +6,17 @@ icon: hammer
|
||||
|
||||
## Creating and Utilizing Tools in CrewAI
|
||||
|
||||
This guide provides detailed instructions on creating custom tools for the CrewAI framework and how to efficiently manage and utilize these tools,
|
||||
incorporating the latest functionalities such as tool delegation, error handling, and dynamic tool calling. It also highlights the importance of collaboration tools,
|
||||
This guide provides detailed instructions on creating custom tools for the CrewAI framework and how to efficiently manage and utilize these tools,
|
||||
incorporating the latest functionalities such as tool delegation, error handling, and dynamic tool calling. It also highlights the importance of collaboration tools,
|
||||
enabling agents to perform a wide range of actions.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before creating your own tools, ensure you have the crewAI extra tools package installed:
|
||||
|
||||
```bash
|
||||
pip install 'crewai[tools]'
|
||||
```
|
||||
|
||||
### Subclassing `BaseTool`
|
||||
|
||||
To create a personalized tool, inherit from `BaseTool` and define the necessary attributes, including the `args_schema` for input validation, and the `_run` method.
|
||||
|
||||
```python Code
|
||||
from typing import Type
|
||||
from crewai_tools import BaseTool
|
||||
from crewai.tools import BaseTool
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class MyToolInput(BaseModel):
|
||||
@@ -47,7 +39,7 @@ Alternatively, you can use the tool decorator `@tool`. This approach allows you
|
||||
offering a concise and efficient way to create specialized tools tailored to your needs.
|
||||
|
||||
```python Code
|
||||
from crewai_tools import tool
|
||||
from crewai.tools import tool
|
||||
|
||||
@tool("Tool Name")
|
||||
def my_simple_tool(question: str) -> str:
|
||||
@@ -73,5 +65,5 @@ def my_cache_strategy(arguments: dict, result: str) -> bool:
|
||||
cached_tool.cache_function = my_cache_strategy
|
||||
```
|
||||
|
||||
By adhering to these guidelines and incorporating new functionalities and collaboration tools into your tool creation and management processes,
|
||||
By adhering to these guidelines and incorporating new functionalities and collaboration tools into your tool creation and management processes,
|
||||
you can leverage the full capabilities of the CrewAI framework, enhancing both the development experience and the efficiency of your AI agents.
|
||||
|
||||
@@ -330,4 +330,4 @@ This will clear the crew's memory, allowing for a fresh start.
|
||||
|
||||
## Deploying Your Project
|
||||
|
||||
The easiest way to deploy your crew is through [CrewAI Enterprise](https://www.crewai.com/crewaiplus), where you can deploy your crew in a few clicks.
|
||||
The easiest way to deploy your crew is through [CrewAI Enterprise](http://app.crewai.com/), where you can deploy your crew in a few clicks.
|
||||
|
||||
@@ -34,6 +34,7 @@ from crewai_tools import GithubSearchTool
|
||||
# Initialize the tool for semantic searches within a specific GitHub repository
|
||||
tool = GithubSearchTool(
|
||||
github_repo='https://github.com/example/repo',
|
||||
gh_token='your_github_personal_access_token',
|
||||
content_types=['code', 'issue'] # Options: code, repo, pr, issue
|
||||
)
|
||||
|
||||
@@ -41,6 +42,7 @@ tool = GithubSearchTool(
|
||||
|
||||
# Initialize the tool for semantic searches within a specific GitHub repository, so the agent can search any repository if it learns about during its execution
|
||||
tool = GithubSearchTool(
|
||||
gh_token='your_github_personal_access_token',
|
||||
content_types=['code', 'issue'] # Options: code, repo, pr, issue
|
||||
)
|
||||
```
|
||||
@@ -48,6 +50,7 @@ tool = GithubSearchTool(
|
||||
## Arguments
|
||||
|
||||
- `github_repo` : The URL of the GitHub repository where the search will be conducted. This is a mandatory field and specifies the target repository for your search.
|
||||
- `gh_token` : Your GitHub Personal Access Token (PAT) required for authentication. You can create one in your GitHub account settings under Developer Settings > Personal Access Tokens.
|
||||
- `content_types` : Specifies the types of content to include in your search. You must provide a list of content types from the following options: `code` for searching within the code,
|
||||
`repo` for searching within the repository's general information, `pr` for searching within pull requests, and `issue` for searching within issues.
|
||||
This field is mandatory and allows tailoring the search to specific content types within the GitHub repository.
|
||||
@@ -77,5 +80,4 @@ tool = GithubSearchTool(
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
```
|
||||
)
|
||||
6
poetry.lock
generated
6
poetry.lock
generated
@@ -1597,12 +1597,12 @@ files = [
|
||||
google-auth = ">=2.14.1,<3.0.dev0"
|
||||
googleapis-common-protos = ">=1.56.2,<2.0.dev0"
|
||||
grpcio = [
|
||||
{version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
|
||||
{version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""},
|
||||
{version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
|
||||
]
|
||||
grpcio-status = [
|
||||
{version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
|
||||
{version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""},
|
||||
{version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
|
||||
]
|
||||
proto-plus = ">=1.22.3,<2.0.0dev"
|
||||
protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0"
|
||||
@@ -4286,8 +4286,8 @@ files = [
|
||||
|
||||
[package.dependencies]
|
||||
numpy = [
|
||||
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
|
||||
{version = ">=1.22.4", markers = "python_version < \"3.11\""},
|
||||
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
|
||||
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
|
||||
]
|
||||
python-dateutil = ">=2.8.2"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "crewai"
|
||||
version = "0.76.9"
|
||||
version = "0.80.0"
|
||||
description = "Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By fostering collaborative intelligence, CrewAI empowers agents to work together seamlessly, tackling complex tasks."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10,<=3.13"
|
||||
@@ -16,7 +16,7 @@ dependencies = [
|
||||
"opentelemetry-exporter-otlp-proto-http>=1.22.0",
|
||||
"instructor>=1.3.3",
|
||||
"regex>=2024.9.11",
|
||||
"crewai-tools>=0.13.4",
|
||||
"crewai-tools>=0.14.0",
|
||||
"click>=8.1.7",
|
||||
"python-dotenv>=1.0.0",
|
||||
"appdirs>=1.4.4",
|
||||
@@ -27,8 +27,8 @@ dependencies = [
|
||||
"pyvis>=0.3.2",
|
||||
"uv>=0.4.25",
|
||||
"tomli-w>=1.1.0",
|
||||
"chromadb>=0.4.24",
|
||||
"tomli>=2.0.2",
|
||||
"chromadb>=0.5.18",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
@@ -37,8 +37,19 @@ Documentation = "https://docs.crewai.com"
|
||||
Repository = "https://github.com/crewAIInc/crewAI"
|
||||
|
||||
[project.optional-dependencies]
|
||||
tools = ["crewai-tools>=0.13.4"]
|
||||
tools = ["crewai-tools>=0.14.0"]
|
||||
agentops = ["agentops>=0.3.0"]
|
||||
fastembed = ["fastembed>=0.4.1"]
|
||||
pdfplumber = [
|
||||
"pdfplumber>=0.11.4",
|
||||
]
|
||||
pandas = [
|
||||
"pandas>=2.2.3",
|
||||
]
|
||||
openpyxl = [
|
||||
"openpyxl>=3.1.5",
|
||||
]
|
||||
mem0 = ["mem0ai>=0.1.29"]
|
||||
|
||||
[tool.uv]
|
||||
dev-dependencies = [
|
||||
@@ -52,7 +63,7 @@ dev-dependencies = [
|
||||
"mkdocs-material-extensions>=1.3.1",
|
||||
"pillow>=10.2.0",
|
||||
"cairosvg>=2.7.1",
|
||||
"crewai-tools>=0.13.4",
|
||||
"crewai-tools>=0.14.0",
|
||||
"pytest>=8.0.0",
|
||||
"pytest-vcr>=1.0.2",
|
||||
"python-dotenv>=1.0.0",
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import warnings
|
||||
|
||||
from crewai.agent import Agent
|
||||
from crewai.crew import Crew
|
||||
from crewai.flow.flow import Flow
|
||||
from crewai.knowledge.knowledge import Knowledge
|
||||
from crewai.llm import LLM
|
||||
from crewai.pipeline import Pipeline
|
||||
from crewai.process import Process
|
||||
@@ -14,5 +16,15 @@ warnings.filterwarnings(
|
||||
category=UserWarning,
|
||||
module="pydantic.main",
|
||||
)
|
||||
__version__ = "0.76.9"
|
||||
__all__ = ["Agent", "Crew", "Process", "Task", "Pipeline", "Router", "LLM", "Flow"]
|
||||
__version__ = "0.80.0"
|
||||
__all__ = [
|
||||
"Agent",
|
||||
"Crew",
|
||||
"Process",
|
||||
"Task",
|
||||
"Pipeline",
|
||||
"Router",
|
||||
"LLM",
|
||||
"Flow",
|
||||
"Knowledge",
|
||||
]
|
||||
|
||||
@@ -8,9 +8,11 @@ from pydantic import Field, InstanceOf, PrivateAttr, model_validator
|
||||
from crewai.agents import CacheHandler
|
||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||
from crewai.agents.crew_agent_executor import CrewAgentExecutor
|
||||
from crewai.cli.constants import ENV_VARS
|
||||
from crewai.llm import LLM
|
||||
from crewai.memory.contextual.contextual_memory import ContextualMemory
|
||||
from crewai.tools.agent_tools import AgentTools
|
||||
from crewai.tools import BaseTool
|
||||
from crewai.tools.agent_tools.agent_tools import AgentTools
|
||||
from crewai.utilities import Converter, Prompts
|
||||
from crewai.utilities.constants import TRAINED_AGENTS_DATA_FILE, TRAINING_DATA_FILE
|
||||
from crewai.utilities.token_counter_callback import TokenCalcHandler
|
||||
@@ -50,6 +52,7 @@ class Agent(BaseAgent):
|
||||
role: The role of the agent.
|
||||
goal: The objective of the agent.
|
||||
backstory: The backstory of the agent.
|
||||
knowledge: The knowledge base of the agent.
|
||||
config: Dict representation of agent configuration.
|
||||
llm: The language model that will run the agent.
|
||||
function_calling_llm: The language model that will handle the tool calling for this agent, it overrides the crew function_calling_llm.
|
||||
@@ -121,6 +124,11 @@ class Agent(BaseAgent):
|
||||
@model_validator(mode="after")
|
||||
def post_init_setup(self):
|
||||
self.agent_ops_agent_name = self.role
|
||||
unnacepted_attributes = [
|
||||
"AWS_ACCESS_KEY_ID",
|
||||
"AWS_SECRET_ACCESS_KEY",
|
||||
"AWS_REGION_NAME",
|
||||
]
|
||||
|
||||
# Handle different cases for self.llm
|
||||
if isinstance(self.llm, str):
|
||||
@@ -130,8 +138,12 @@ class Agent(BaseAgent):
|
||||
# If it's already an LLM instance, keep it as is
|
||||
pass
|
||||
elif self.llm is None:
|
||||
# If it's None, use environment variables or default
|
||||
model_name = os.environ.get("OPENAI_MODEL_NAME", "gpt-4o-mini")
|
||||
# Determine the model name from environment variables or use default
|
||||
model_name = (
|
||||
os.environ.get("OPENAI_MODEL_NAME")
|
||||
or os.environ.get("MODEL")
|
||||
or "gpt-4o-mini"
|
||||
)
|
||||
llm_params = {"model": model_name}
|
||||
|
||||
api_base = os.environ.get("OPENAI_API_BASE") or os.environ.get(
|
||||
@@ -140,9 +152,44 @@ class Agent(BaseAgent):
|
||||
if api_base:
|
||||
llm_params["base_url"] = api_base
|
||||
|
||||
api_key = os.environ.get("OPENAI_API_KEY")
|
||||
if api_key:
|
||||
llm_params["api_key"] = api_key
|
||||
set_provider = model_name.split("/")[0] if "/" in model_name else "openai"
|
||||
|
||||
# Iterate over all environment variables to find matching API keys or use defaults
|
||||
for provider, env_vars in ENV_VARS.items():
|
||||
if provider == set_provider:
|
||||
for env_var in env_vars:
|
||||
if env_var["key_name"] in unnacepted_attributes:
|
||||
continue
|
||||
# Check if the environment variable is set
|
||||
if "key_name" in env_var:
|
||||
env_value = os.environ.get(env_var["key_name"])
|
||||
if env_value:
|
||||
# Map key names containing "API_KEY" to "api_key"
|
||||
key_name = (
|
||||
"api_key"
|
||||
if "API_KEY" in env_var["key_name"]
|
||||
else env_var["key_name"]
|
||||
)
|
||||
# Map key names containing "API_BASE" to "api_base"
|
||||
key_name = (
|
||||
"api_base"
|
||||
if "API_BASE" in env_var["key_name"]
|
||||
else key_name
|
||||
)
|
||||
# Map key names containing "API_VERSION" to "api_version"
|
||||
key_name = (
|
||||
"api_version"
|
||||
if "API_VERSION" in env_var["key_name"]
|
||||
else key_name
|
||||
)
|
||||
llm_params[key_name] = env_value
|
||||
# Check for default values if the environment variable is not set
|
||||
elif env_var.get("default", False):
|
||||
for key, value in env_var.items():
|
||||
if key not in ["prompt", "key_name", "default"]:
|
||||
# Only add default if the key is already set in os.environ
|
||||
if key in os.environ:
|
||||
llm_params[key] = value
|
||||
|
||||
self.llm = LLM(**llm_params)
|
||||
else:
|
||||
@@ -192,7 +239,7 @@ class Agent(BaseAgent):
|
||||
self,
|
||||
task: Any,
|
||||
context: Optional[str] = None,
|
||||
tools: Optional[List[Any]] = None,
|
||||
tools: Optional[List[BaseTool]] = None,
|
||||
) -> str:
|
||||
"""Execute a task with the agent.
|
||||
|
||||
@@ -216,14 +263,28 @@ class Agent(BaseAgent):
|
||||
|
||||
if self.crew and self.crew.memory:
|
||||
contextual_memory = ContextualMemory(
|
||||
self.crew.memory_config,
|
||||
self.crew._short_term_memory,
|
||||
self.crew._long_term_memory,
|
||||
self.crew._entity_memory,
|
||||
self.crew._user_memory,
|
||||
)
|
||||
memory = contextual_memory.build_context_for_task(task, context)
|
||||
if memory.strip() != "":
|
||||
task_prompt += self.i18n.slice("memory").format(memory=memory)
|
||||
|
||||
# Integrate the knowledge base
|
||||
if self.crew and self.crew.knowledge:
|
||||
knowledge_snippets = self.crew.knowledge.query([task.prompt()])
|
||||
valid_snippets = [
|
||||
result["context"]
|
||||
for result in knowledge_snippets
|
||||
if result and result.get("context")
|
||||
]
|
||||
if valid_snippets:
|
||||
formatted_knowledge = "\n".join(valid_snippets)
|
||||
task_prompt += f"\n\nAdditional Information:\n{formatted_knowledge}"
|
||||
|
||||
tools = tools or self.tools or []
|
||||
self.create_agent_executor(tools=tools, task=task)
|
||||
|
||||
@@ -259,7 +320,9 @@ class Agent(BaseAgent):
|
||||
|
||||
return result
|
||||
|
||||
def create_agent_executor(self, tools=None, task=None) -> None:
|
||||
def create_agent_executor(
|
||||
self, tools: Optional[List[BaseTool]] = None, task=None
|
||||
) -> None:
|
||||
"""Create an agent executor for the agent.
|
||||
|
||||
Returns:
|
||||
@@ -332,7 +395,7 @@ class Agent(BaseAgent):
|
||||
tools_list = []
|
||||
try:
|
||||
# tentatively try to import from crewai_tools import BaseTool as CrewAITool
|
||||
from crewai_tools import BaseTool as CrewAITool
|
||||
from crewai.tools import BaseTool as CrewAITool
|
||||
|
||||
for tool in tools:
|
||||
if isinstance(tool, CrewAITool):
|
||||
@@ -391,7 +454,7 @@ class Agent(BaseAgent):
|
||||
|
||||
return description
|
||||
|
||||
def _render_text_description_and_args(self, tools: List[Any]) -> str:
|
||||
def _render_text_description_and_args(self, tools: List[BaseTool]) -> str:
|
||||
"""Render the tool name, description, and args in plain text.
|
||||
|
||||
Output will be in the format of:
|
||||
@@ -404,17 +467,7 @@ class Agent(BaseAgent):
|
||||
"""
|
||||
tool_strings = []
|
||||
for tool in tools:
|
||||
args_schema = {
|
||||
name: {
|
||||
"description": field.description,
|
||||
"type": field.annotation.__name__,
|
||||
}
|
||||
for name, field in tool.args_schema.model_fields.items()
|
||||
}
|
||||
description = (
|
||||
f"Tool Name: {tool.name}\nTool Description: {tool.description}"
|
||||
)
|
||||
tool_strings.append(f"{description}\nTool Arguments: {args_schema}")
|
||||
tool_strings.append(tool.description)
|
||||
|
||||
return "\n".join(tool_strings)
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ from pydantic_core import PydanticCustomError
|
||||
from crewai.agents.agent_builder.utilities.base_token_process import TokenProcess
|
||||
from crewai.agents.cache.cache_handler import CacheHandler
|
||||
from crewai.agents.tools_handler import ToolsHandler
|
||||
from crewai.tools import BaseTool
|
||||
from crewai.utilities import I18N, Logger, RPMController
|
||||
from crewai.utilities.config import process_config
|
||||
|
||||
@@ -49,11 +50,11 @@ class BaseAgent(ABC, BaseModel):
|
||||
|
||||
|
||||
Methods:
|
||||
execute_task(task: Any, context: Optional[str] = None, tools: Optional[List[Any]] = None) -> str:
|
||||
execute_task(task: Any, context: Optional[str] = None, tools: Optional[List[BaseTool]] = None) -> str:
|
||||
Abstract method to execute a task.
|
||||
create_agent_executor(tools=None) -> None:
|
||||
Abstract method to create an agent executor.
|
||||
_parse_tools(tools: List[Any]) -> List[Any]:
|
||||
_parse_tools(tools: List[BaseTool]) -> List[Any]:
|
||||
Abstract method to parse tools.
|
||||
get_delegation_tools(agents: List["BaseAgent"]):
|
||||
Abstract method to set the agents task tools for handling delegation and question asking to other agents in crew.
|
||||
@@ -105,7 +106,7 @@ class BaseAgent(ABC, BaseModel):
|
||||
default=False,
|
||||
description="Enable agent to delegate and ask questions among each other.",
|
||||
)
|
||||
tools: Optional[List[Any]] = Field(
|
||||
tools: Optional[List[BaseTool]] = Field(
|
||||
default_factory=list, description="Tools at agents' disposal"
|
||||
)
|
||||
max_iter: Optional[int] = Field(
|
||||
@@ -188,7 +189,7 @@ class BaseAgent(ABC, BaseModel):
|
||||
self,
|
||||
task: Any,
|
||||
context: Optional[str] = None,
|
||||
tools: Optional[List[Any]] = None,
|
||||
tools: Optional[List[BaseTool]] = None,
|
||||
) -> str:
|
||||
pass
|
||||
|
||||
@@ -197,11 +198,11 @@ class BaseAgent(ABC, BaseModel):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def _parse_tools(self, tools: List[Any]) -> List[Any]:
|
||||
def _parse_tools(self, tools: List[BaseTool]) -> List[BaseTool]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_delegation_tools(self, agents: List["BaseAgent"]) -> List[Any]:
|
||||
def get_delegation_tools(self, agents: List["BaseAgent"]) -> List[BaseTool]:
|
||||
"""Set the task tools that init BaseAgenTools class."""
|
||||
pass
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ from crewai.types.usage_metrics import UsageMetrics
|
||||
class TokenProcess:
|
||||
total_tokens: int = 0
|
||||
prompt_tokens: int = 0
|
||||
cached_prompt_tokens: int = 0
|
||||
completion_tokens: int = 0
|
||||
successful_requests: int = 0
|
||||
|
||||
@@ -15,6 +16,9 @@ class TokenProcess:
|
||||
self.completion_tokens = self.completion_tokens + tokens
|
||||
self.total_tokens = self.total_tokens + tokens
|
||||
|
||||
def sum_cached_prompt_tokens(self, tokens: int):
|
||||
self.cached_prompt_tokens = self.cached_prompt_tokens + tokens
|
||||
|
||||
def sum_successful_requests(self, requests: int):
|
||||
self.successful_requests = self.successful_requests + requests
|
||||
|
||||
@@ -22,6 +26,7 @@ class TokenProcess:
|
||||
return UsageMetrics(
|
||||
total_tokens=self.total_tokens,
|
||||
prompt_tokens=self.prompt_tokens,
|
||||
cached_prompt_tokens=self.cached_prompt_tokens,
|
||||
completion_tokens=self.completion_tokens,
|
||||
successful_requests=self.successful_requests,
|
||||
)
|
||||
|
||||
@@ -117,6 +117,15 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
||||
callbacks=self.callbacks,
|
||||
)
|
||||
|
||||
if answer is None or answer == "":
|
||||
self._printer.print(
|
||||
content="Received None or empty response from LLM call.",
|
||||
color="red",
|
||||
)
|
||||
raise ValueError(
|
||||
"Invalid response from LLM call - None or empty."
|
||||
)
|
||||
|
||||
if not self.use_stop_words:
|
||||
try:
|
||||
self._format_answer(answer)
|
||||
@@ -136,25 +145,26 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
||||
formatted_answer.result = action_result
|
||||
self._show_logs(formatted_answer)
|
||||
|
||||
if self.step_callback:
|
||||
self.step_callback(formatted_answer)
|
||||
if self.step_callback:
|
||||
self.step_callback(formatted_answer)
|
||||
|
||||
if self._should_force_answer():
|
||||
if self.have_forced_answer:
|
||||
return AgentFinish(
|
||||
output=self._i18n.errors(
|
||||
"force_final_answer_error"
|
||||
).format(formatted_answer.text),
|
||||
text=formatted_answer.text,
|
||||
)
|
||||
else:
|
||||
formatted_answer.text += (
|
||||
f'\n{self._i18n.errors("force_final_answer")}'
|
||||
)
|
||||
self.have_forced_answer = True
|
||||
self.messages.append(
|
||||
self._format_msg(formatted_answer.text, role="assistant")
|
||||
)
|
||||
if self._should_force_answer():
|
||||
if self.have_forced_answer:
|
||||
return AgentFinish(
|
||||
thought="",
|
||||
output=self._i18n.errors(
|
||||
"force_final_answer_error"
|
||||
).format(formatted_answer.text),
|
||||
text=formatted_answer.text,
|
||||
)
|
||||
else:
|
||||
formatted_answer.text += (
|
||||
f'\n{self._i18n.errors("force_final_answer")}'
|
||||
)
|
||||
self.have_forced_answer = True
|
||||
self.messages.append(
|
||||
self._format_msg(formatted_answer.text, role="assistant")
|
||||
)
|
||||
|
||||
except OutputParserException as e:
|
||||
self.messages.append({"role": "user", "content": e.error})
|
||||
@@ -323,9 +333,9 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
||||
if self.crew is not None and hasattr(self.crew, "_train_iteration"):
|
||||
train_iteration = self.crew._train_iteration
|
||||
if agent_id in training_data and isinstance(train_iteration, int):
|
||||
training_data[agent_id][train_iteration][
|
||||
"improved_output"
|
||||
] = result.output
|
||||
training_data[agent_id][train_iteration]["improved_output"] = (
|
||||
result.output
|
||||
)
|
||||
training_handler.save(training_data)
|
||||
else:
|
||||
self._logger.log(
|
||||
@@ -376,4 +386,5 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
||||
return CrewAgentParser(agent=self.agent).parse(answer)
|
||||
|
||||
def _format_msg(self, prompt: str, role: str = "user") -> Dict[str, str]:
|
||||
prompt = prompt.rstrip()
|
||||
return {"role": role, "content": prompt}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
from ..tools.cache_tools import CacheTools
|
||||
from ..tools.cache_tools.cache_tools import CacheTools
|
||||
from ..tools.tool_calling import InstructorToolCalling, ToolCalling
|
||||
from .cache.cache_handler import CacheHandler
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ def create_embedded_crew(crew_name: str, parent_folder: Path) -> None:
|
||||
|
||||
templates_dir = Path(__file__).parent / "templates" / "crew"
|
||||
config_template_files = ["agents.yaml", "tasks.yaml"]
|
||||
crew_template_file = f"{folder_name}_crew.py" # Updated file name
|
||||
crew_template_file = f"{folder_name}.py" # Updated file name
|
||||
|
||||
for file_name in config_template_files:
|
||||
src_file = templates_dir / "config" / file_name
|
||||
|
||||
@@ -34,7 +34,9 @@ class AuthenticationCommand:
|
||||
"scope": "openid",
|
||||
"audience": AUTH0_AUDIENCE,
|
||||
}
|
||||
response = requests.post(url=self.DEVICE_CODE_URL, data=device_code_payload)
|
||||
response = requests.post(
|
||||
url=self.DEVICE_CODE_URL, data=device_code_payload, timeout=20
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
@@ -54,7 +56,7 @@ class AuthenticationCommand:
|
||||
|
||||
attempts = 0
|
||||
while True and attempts < 5:
|
||||
response = requests.post(self.TOKEN_URL, data=token_payload)
|
||||
response = requests.post(self.TOKEN_URL, data=token_payload, timeout=30)
|
||||
token_data = response.json()
|
||||
|
||||
if response.status_code == 200:
|
||||
|
||||
@@ -136,6 +136,7 @@ def log_tasks_outputs() -> None:
|
||||
@click.option("-l", "--long", is_flag=True, help="Reset LONG TERM memory")
|
||||
@click.option("-s", "--short", is_flag=True, help="Reset SHORT TERM memory")
|
||||
@click.option("-e", "--entities", is_flag=True, help="Reset ENTITIES memory")
|
||||
@click.option("-kn", "--knowledge", is_flag=True, help="Reset KNOWLEDGE storage")
|
||||
@click.option(
|
||||
"-k",
|
||||
"--kickoff-outputs",
|
||||
@@ -143,17 +144,24 @@ def log_tasks_outputs() -> None:
|
||||
help="Reset LATEST KICKOFF TASK OUTPUTS",
|
||||
)
|
||||
@click.option("-a", "--all", is_flag=True, help="Reset ALL memories")
|
||||
def reset_memories(long, short, entities, kickoff_outputs, all):
|
||||
def reset_memories(
|
||||
long: bool,
|
||||
short: bool,
|
||||
entities: bool,
|
||||
knowledge: bool,
|
||||
kickoff_outputs: bool,
|
||||
all: bool,
|
||||
) -> None:
|
||||
"""
|
||||
Reset the crew memories (long, short, entity, latest_crew_kickoff_ouputs). This will delete all the data saved.
|
||||
"""
|
||||
try:
|
||||
if not all and not (long or short or entities or kickoff_outputs):
|
||||
if not all and not (long or short or entities or knowledge or kickoff_outputs):
|
||||
click.echo(
|
||||
"Please specify at least one memory type to reset using the appropriate flags."
|
||||
)
|
||||
return
|
||||
reset_memories_command(long, short, entities, kickoff_outputs, all)
|
||||
reset_memories_command(long, short, entities, knowledge, kickoff_outputs, all)
|
||||
except Exception as e:
|
||||
click.echo(f"An error occurred while resetting memories: {e}", err=True)
|
||||
|
||||
|
||||
@@ -1,19 +1,161 @@
|
||||
ENV_VARS = {
|
||||
'openai': ['OPENAI_API_KEY'],
|
||||
'anthropic': ['ANTHROPIC_API_KEY'],
|
||||
'gemini': ['GEMINI_API_KEY'],
|
||||
'groq': ['GROQ_API_KEY'],
|
||||
'ollama': ['FAKE_KEY'],
|
||||
"openai": [
|
||||
{
|
||||
"prompt": "Enter your OPENAI API key (press Enter to skip)",
|
||||
"key_name": "OPENAI_API_KEY",
|
||||
}
|
||||
],
|
||||
"anthropic": [
|
||||
{
|
||||
"prompt": "Enter your ANTHROPIC API key (press Enter to skip)",
|
||||
"key_name": "ANTHROPIC_API_KEY",
|
||||
}
|
||||
],
|
||||
"gemini": [
|
||||
{
|
||||
"prompt": "Enter your GEMINI API key (press Enter to skip)",
|
||||
"key_name": "GEMINI_API_KEY",
|
||||
}
|
||||
],
|
||||
"groq": [
|
||||
{
|
||||
"prompt": "Enter your GROQ API key (press Enter to skip)",
|
||||
"key_name": "GROQ_API_KEY",
|
||||
}
|
||||
],
|
||||
"watson": [
|
||||
{
|
||||
"prompt": "Enter your WATSONX URL (press Enter to skip)",
|
||||
"key_name": "WATSONX_URL",
|
||||
},
|
||||
{
|
||||
"prompt": "Enter your WATSONX API Key (press Enter to skip)",
|
||||
"key_name": "WATSONX_APIKEY",
|
||||
},
|
||||
{
|
||||
"prompt": "Enter your WATSONX Project Id (press Enter to skip)",
|
||||
"key_name": "WATSONX_PROJECT_ID",
|
||||
},
|
||||
],
|
||||
"ollama": [
|
||||
{
|
||||
"default": True,
|
||||
"API_BASE": "http://localhost:11434",
|
||||
}
|
||||
],
|
||||
"bedrock": [
|
||||
{
|
||||
"prompt": "Enter your AWS Access Key ID (press Enter to skip)",
|
||||
"key_name": "AWS_ACCESS_KEY_ID",
|
||||
},
|
||||
{
|
||||
"prompt": "Enter your AWS Secret Access Key (press Enter to skip)",
|
||||
"key_name": "AWS_SECRET_ACCESS_KEY",
|
||||
},
|
||||
{
|
||||
"prompt": "Enter your AWS Region Name (press Enter to skip)",
|
||||
"key_name": "AWS_REGION_NAME",
|
||||
},
|
||||
],
|
||||
"azure": [
|
||||
{
|
||||
"prompt": "Enter your Azure deployment name (must start with 'azure/')",
|
||||
"key_name": "model",
|
||||
},
|
||||
{
|
||||
"prompt": "Enter your AZURE API key (press Enter to skip)",
|
||||
"key_name": "AZURE_API_KEY",
|
||||
},
|
||||
{
|
||||
"prompt": "Enter your AZURE API base URL (press Enter to skip)",
|
||||
"key_name": "AZURE_API_BASE",
|
||||
},
|
||||
{
|
||||
"prompt": "Enter your AZURE API version (press Enter to skip)",
|
||||
"key_name": "AZURE_API_VERSION",
|
||||
},
|
||||
],
|
||||
"cerebras": [
|
||||
{
|
||||
"prompt": "Enter your Cerebras model name (must start with 'cerebras/')",
|
||||
"key_name": "model",
|
||||
},
|
||||
{
|
||||
"prompt": "Enter your Cerebras API version (press Enter to skip)",
|
||||
"key_name": "CEREBRAS_API_KEY",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
PROVIDERS = ['openai', 'anthropic', 'gemini', 'groq', 'ollama']
|
||||
|
||||
PROVIDERS = [
|
||||
"openai",
|
||||
"anthropic",
|
||||
"gemini",
|
||||
"groq",
|
||||
"ollama",
|
||||
"watson",
|
||||
"bedrock",
|
||||
"azure",
|
||||
"cerebras",
|
||||
]
|
||||
|
||||
MODELS = {
|
||||
'openai': ['gpt-4', 'gpt-4o', 'gpt-4o-mini', 'o1-mini', 'o1-preview'],
|
||||
'anthropic': ['claude-3-5-sonnet-20240620', 'claude-3-sonnet-20240229', 'claude-3-opus-20240229', 'claude-3-haiku-20240307'],
|
||||
'gemini': ['gemini-1.5-flash', 'gemini-1.5-pro', 'gemini-gemma-2-9b-it', 'gemini-gemma-2-27b-it'],
|
||||
'groq': ['llama-3.1-8b-instant', 'llama-3.1-70b-versatile', 'llama-3.1-405b-reasoning', 'gemma2-9b-it', 'gemma-7b-it'],
|
||||
'ollama': ['llama3.1', 'mixtral'],
|
||||
"openai": ["gpt-4", "gpt-4o", "gpt-4o-mini", "o1-mini", "o1-preview"],
|
||||
"anthropic": [
|
||||
"claude-3-5-sonnet-20240620",
|
||||
"claude-3-sonnet-20240229",
|
||||
"claude-3-opus-20240229",
|
||||
"claude-3-haiku-20240307",
|
||||
],
|
||||
"gemini": [
|
||||
"gemini/gemini-1.5-flash",
|
||||
"gemini/gemini-1.5-pro",
|
||||
"gemini/gemini-gemma-2-9b-it",
|
||||
"gemini/gemini-gemma-2-27b-it",
|
||||
],
|
||||
"groq": [
|
||||
"groq/llama-3.1-8b-instant",
|
||||
"groq/llama-3.1-70b-versatile",
|
||||
"groq/llama-3.1-405b-reasoning",
|
||||
"groq/gemma2-9b-it",
|
||||
"groq/gemma-7b-it",
|
||||
],
|
||||
"ollama": ["ollama/llama3.1", "ollama/mixtral"],
|
||||
"watson": [
|
||||
"watsonx/meta-llama/llama-3-1-70b-instruct",
|
||||
"watsonx/meta-llama/llama-3-1-8b-instruct",
|
||||
"watsonx/meta-llama/llama-3-2-11b-vision-instruct",
|
||||
"watsonx/meta-llama/llama-3-2-1b-instruct",
|
||||
"watsonx/meta-llama/llama-3-2-90b-vision-instruct",
|
||||
"watsonx/meta-llama/llama-3-405b-instruct",
|
||||
"watsonx/mistral/mistral-large",
|
||||
"watsonx/ibm/granite-3-8b-instruct",
|
||||
],
|
||||
"bedrock": [
|
||||
"bedrock/anthropic.claude-3-5-sonnet-20240620-v1:0",
|
||||
"bedrock/anthropic.claude-3-sonnet-20240229-v1:0",
|
||||
"bedrock/anthropic.claude-3-haiku-20240307-v1:0",
|
||||
"bedrock/anthropic.claude-3-opus-20240229-v1:0",
|
||||
"bedrock/anthropic.claude-v2:1",
|
||||
"bedrock/anthropic.claude-v2",
|
||||
"bedrock/anthropic.claude-instant-v1",
|
||||
"bedrock/meta.llama3-1-405b-instruct-v1:0",
|
||||
"bedrock/meta.llama3-1-70b-instruct-v1:0",
|
||||
"bedrock/meta.llama3-1-8b-instruct-v1:0",
|
||||
"bedrock/meta.llama3-70b-instruct-v1:0",
|
||||
"bedrock/meta.llama3-8b-instruct-v1:0",
|
||||
"bedrock/amazon.titan-text-lite-v1",
|
||||
"bedrock/amazon.titan-text-express-v1",
|
||||
"bedrock/cohere.command-text-v14",
|
||||
"bedrock/ai21.j2-mid-v1",
|
||||
"bedrock/ai21.j2-ultra-v1",
|
||||
"bedrock/ai21.jamba-instruct-v1:0",
|
||||
"bedrock/meta.llama2-13b-chat-v1",
|
||||
"bedrock/meta.llama2-70b-chat-v1",
|
||||
"bedrock/mistral.mistral-7b-instruct-v0:2",
|
||||
"bedrock/mistral.mixtral-8x7b-instruct-v0:1",
|
||||
],
|
||||
}
|
||||
|
||||
JSON_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
|
||||
JSON_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import click
|
||||
|
||||
from crewai.cli.constants import ENV_VARS
|
||||
from crewai.cli.constants import ENV_VARS, MODELS
|
||||
from crewai.cli.provider import (
|
||||
PROVIDERS,
|
||||
get_provider_data,
|
||||
select_model,
|
||||
select_provider,
|
||||
@@ -29,20 +29,20 @@ def create_folder_structure(name, parent_folder=None):
|
||||
click.secho("Operation cancelled.", fg="yellow")
|
||||
sys.exit(0)
|
||||
click.secho(f"Overriding folder {folder_name}...", fg="green", bold=True)
|
||||
else:
|
||||
click.secho(
|
||||
f"Creating {'crew' if parent_folder else 'folder'} {folder_name}...",
|
||||
fg="green",
|
||||
bold=True,
|
||||
)
|
||||
shutil.rmtree(folder_path) # Delete the existing folder and its contents
|
||||
|
||||
if not folder_path.exists():
|
||||
folder_path.mkdir(parents=True)
|
||||
(folder_path / "tests").mkdir(exist_ok=True)
|
||||
if not parent_folder:
|
||||
(folder_path / "src" / folder_name).mkdir(parents=True)
|
||||
(folder_path / "src" / folder_name / "tools").mkdir(parents=True)
|
||||
(folder_path / "src" / folder_name / "config").mkdir(parents=True)
|
||||
click.secho(
|
||||
f"Creating {'crew' if parent_folder else 'folder'} {folder_name}...",
|
||||
fg="green",
|
||||
bold=True,
|
||||
)
|
||||
|
||||
folder_path.mkdir(parents=True)
|
||||
(folder_path / "tests").mkdir(exist_ok=True)
|
||||
if not parent_folder:
|
||||
(folder_path / "src" / folder_name).mkdir(parents=True)
|
||||
(folder_path / "src" / folder_name / "tools").mkdir(parents=True)
|
||||
(folder_path / "src" / folder_name / "config").mkdir(parents=True)
|
||||
|
||||
return folder_path, folder_name, class_name
|
||||
|
||||
@@ -92,7 +92,10 @@ def create_crew(name, provider=None, skip_provider=False, parent_folder=None):
|
||||
|
||||
existing_provider = None
|
||||
for provider, env_keys in ENV_VARS.items():
|
||||
if any(key in env_vars for key in env_keys):
|
||||
if any(
|
||||
"key_name" in details and details["key_name"] in env_vars
|
||||
for details in env_keys
|
||||
):
|
||||
existing_provider = provider
|
||||
break
|
||||
|
||||
@@ -118,47 +121,48 @@ def create_crew(name, provider=None, skip_provider=False, parent_folder=None):
|
||||
"No provider selected. Please try again or press 'q' to exit.", fg="red"
|
||||
)
|
||||
|
||||
while True:
|
||||
selected_model = select_model(selected_provider, provider_models)
|
||||
if selected_model is None: # User typed 'q'
|
||||
click.secho("Exiting...", fg="yellow")
|
||||
sys.exit(0)
|
||||
if selected_model: # Valid selection
|
||||
break
|
||||
click.secho(
|
||||
"No model selected. Please try again or press 'q' to exit.", fg="red"
|
||||
)
|
||||
# Check if the selected provider has predefined models
|
||||
if selected_provider in MODELS and MODELS[selected_provider]:
|
||||
while True:
|
||||
selected_model = select_model(selected_provider, provider_models)
|
||||
if selected_model is None: # User typed 'q'
|
||||
click.secho("Exiting...", fg="yellow")
|
||||
sys.exit(0)
|
||||
if selected_model: # Valid selection
|
||||
break
|
||||
click.secho(
|
||||
"No model selected. Please try again or press 'q' to exit.",
|
||||
fg="red",
|
||||
)
|
||||
env_vars["MODEL"] = selected_model
|
||||
|
||||
if selected_provider in PROVIDERS:
|
||||
api_key_var = ENV_VARS[selected_provider][0]
|
||||
else:
|
||||
api_key_var = click.prompt(
|
||||
f"Enter the environment variable name for your {selected_provider.capitalize()} API key",
|
||||
type=str,
|
||||
default="",
|
||||
)
|
||||
# Check if the selected provider requires API keys
|
||||
if selected_provider in ENV_VARS:
|
||||
provider_env_vars = ENV_VARS[selected_provider]
|
||||
for details in provider_env_vars:
|
||||
if details.get("default", False):
|
||||
# Automatically add default key-value pairs
|
||||
for key, value in details.items():
|
||||
if key not in ["prompt", "key_name", "default"]:
|
||||
env_vars[key] = value
|
||||
elif "key_name" in details:
|
||||
# Prompt for non-default key-value pairs
|
||||
prompt = details["prompt"]
|
||||
key_name = details["key_name"]
|
||||
api_key_value = click.prompt(prompt, default="", show_default=False)
|
||||
|
||||
api_key_value = ""
|
||||
click.echo(
|
||||
f"Enter your {selected_provider.capitalize()} API key (press Enter to skip): ",
|
||||
nl=False,
|
||||
)
|
||||
try:
|
||||
api_key_value = input()
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
api_key_value = ""
|
||||
if api_key_value.strip():
|
||||
env_vars[key_name] = api_key_value
|
||||
|
||||
if api_key_value.strip():
|
||||
env_vars = {api_key_var: api_key_value}
|
||||
if env_vars:
|
||||
write_env_file(folder_path, env_vars)
|
||||
click.secho("API key saved to .env file", fg="green")
|
||||
click.secho("API keys and model saved to .env file", fg="green")
|
||||
else:
|
||||
click.secho(
|
||||
"No API key provided. Skipping .env file creation.", fg="yellow"
|
||||
"No API keys provided. Skipping .env file creation.", fg="yellow"
|
||||
)
|
||||
|
||||
env_vars["MODEL"] = selected_model
|
||||
click.secho(f"Selected model: {selected_model}", fg="green")
|
||||
click.secho(f"Selected model: {env_vars.get('MODEL', 'N/A')}", fg="green")
|
||||
|
||||
package_dir = Path(__file__).parent
|
||||
templates_dir = package_dir / "templates" / "crew"
|
||||
|
||||
@@ -164,7 +164,7 @@ def fetch_provider_data(cache_file):
|
||||
- dict or None: The fetched provider data or None if the operation fails.
|
||||
"""
|
||||
try:
|
||||
response = requests.get(JSON_URL, stream=True, timeout=10)
|
||||
response = requests.get(JSON_URL, stream=True, timeout=60)
|
||||
response.raise_for_status()
|
||||
data = download_data(response)
|
||||
with open(cache_file, "w") as f:
|
||||
|
||||
@@ -5,9 +5,17 @@ from crewai.memory.entity.entity_memory import EntityMemory
|
||||
from crewai.memory.long_term.long_term_memory import LongTermMemory
|
||||
from crewai.memory.short_term.short_term_memory import ShortTermMemory
|
||||
from crewai.utilities.task_output_storage_handler import TaskOutputStorageHandler
|
||||
from crewai.knowledge.storage.knowledge_storage import KnowledgeStorage
|
||||
|
||||
|
||||
def reset_memories_command(long, short, entity, kickoff_outputs, all) -> None:
|
||||
def reset_memories_command(
|
||||
long,
|
||||
short,
|
||||
entity,
|
||||
knowledge,
|
||||
kickoff_outputs,
|
||||
all,
|
||||
) -> None:
|
||||
"""
|
||||
Reset the crew memories.
|
||||
|
||||
@@ -17,6 +25,7 @@ def reset_memories_command(long, short, entity, kickoff_outputs, all) -> None:
|
||||
entity (bool): Whether to reset the entity memory.
|
||||
kickoff_outputs (bool): Whether to reset the latest kickoff task outputs.
|
||||
all (bool): Whether to reset all memories.
|
||||
knowledge (bool): Whether to reset the knowledge.
|
||||
"""
|
||||
|
||||
try:
|
||||
@@ -25,6 +34,7 @@ def reset_memories_command(long, short, entity, kickoff_outputs, all) -> None:
|
||||
EntityMemory().reset()
|
||||
LongTermMemory().reset()
|
||||
TaskOutputStorageHandler().reset()
|
||||
KnowledgeStorage().reset()
|
||||
click.echo("All memories have been reset.")
|
||||
else:
|
||||
if long:
|
||||
@@ -40,6 +50,9 @@ def reset_memories_command(long, short, entity, kickoff_outputs, all) -> None:
|
||||
if kickoff_outputs:
|
||||
TaskOutputStorageHandler().reset()
|
||||
click.echo("Latest Kickoff outputs stored has been reset.")
|
||||
if knowledge:
|
||||
KnowledgeStorage().reset()
|
||||
click.echo("Knowledge has been reset.")
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
click.echo(f"An error occurred while resetting the memories: {e}", err=True)
|
||||
|
||||
@@ -24,7 +24,6 @@ def run_crew() -> None:
|
||||
f"Please run `crewai update` to update your pyproject.toml to use uv.",
|
||||
fg="red",
|
||||
)
|
||||
print()
|
||||
|
||||
try:
|
||||
subprocess.run(command, capture_output=False, text=True, check=True)
|
||||
|
||||
@@ -8,9 +8,12 @@ from crewai.project import CrewBase, agent, crew, task
|
||||
# from crewai_tools import SerperDevTool
|
||||
|
||||
@CrewBase
|
||||
class {{crew_name}}Crew():
|
||||
class {{crew_name}}():
|
||||
"""{{crew_name}} crew"""
|
||||
|
||||
agents_config = 'config/agents.yaml'
|
||||
tasks_config = 'config/tasks.yaml'
|
||||
|
||||
@agent
|
||||
def researcher(self) -> Agent:
|
||||
return Agent(
|
||||
@@ -48,4 +51,4 @@ class {{crew_name}}Crew():
|
||||
process=Process.sequential,
|
||||
verbose=True,
|
||||
# process=Process.hierarchical, # In case you wanna use that instead https://docs.crewai.com/how-to/Hierarchical/
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
from {{folder_name}}.crew import {{crew_name}}Crew
|
||||
import warnings
|
||||
|
||||
from {{folder_name}}.crew import {{crew_name}}
|
||||
|
||||
warnings.filterwarnings("ignore", category=SyntaxWarning, module="pysbd")
|
||||
|
||||
# This main file is intended to be a way for you to run your
|
||||
# crew locally, so refrain from adding unnecessary logic into this file.
|
||||
@@ -14,7 +18,7 @@ def run():
|
||||
inputs = {
|
||||
'topic': 'AI LLMs'
|
||||
}
|
||||
{{crew_name}}Crew().crew().kickoff(inputs=inputs)
|
||||
{{crew_name}}().crew().kickoff(inputs=inputs)
|
||||
|
||||
|
||||
def train():
|
||||
@@ -25,7 +29,7 @@ def train():
|
||||
"topic": "AI LLMs"
|
||||
}
|
||||
try:
|
||||
{{crew_name}}Crew().crew().train(n_iterations=int(sys.argv[1]), filename=sys.argv[2], inputs=inputs)
|
||||
{{crew_name}}().crew().train(n_iterations=int(sys.argv[1]), filename=sys.argv[2], inputs=inputs)
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"An error occurred while training the crew: {e}")
|
||||
@@ -35,7 +39,7 @@ def replay():
|
||||
Replay the crew execution from a specific task.
|
||||
"""
|
||||
try:
|
||||
{{crew_name}}Crew().crew().replay(task_id=sys.argv[1])
|
||||
{{crew_name}}().crew().replay(task_id=sys.argv[1])
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"An error occurred while replaying the crew: {e}")
|
||||
@@ -48,7 +52,7 @@ def test():
|
||||
"topic": "AI LLMs"
|
||||
}
|
||||
try:
|
||||
{{crew_name}}Crew().crew().test(n_iterations=int(sys.argv[1]), openai_model_name=sys.argv[2], inputs=inputs)
|
||||
{{crew_name}}().crew().test(n_iterations=int(sys.argv[1]), openai_model_name=sys.argv[2], inputs=inputs)
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"An error occurred while replaying the crew: {e}")
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
|
||||
authors = [{ name = "Your Name", email = "you@example.com" }]
|
||||
requires-python = ">=3.10,<=3.13"
|
||||
dependencies = [
|
||||
"crewai[tools]>=0.76.9,<1.0.0"
|
||||
"crewai[tools]>=0.80.0,<1.0.0"
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from crewai.tools import BaseTool
|
||||
from typing import Type
|
||||
from crewai_tools import BaseTool
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class MyCustomToolInput(BaseModel):
|
||||
"""Input schema for MyCustomTool."""
|
||||
argument: str = Field(..., description="Description of the argument.")
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
|
||||
authors = [{ name = "Your Name", email = "you@example.com" }]
|
||||
requires-python = ">=3.10,<=3.13"
|
||||
dependencies = [
|
||||
"crewai[tools]>=0.76.9,<1.0.0",
|
||||
"crewai[tools]>=0.80.0,<1.0.0",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import Type
|
||||
|
||||
from crewai_tools import BaseTool
|
||||
from crewai.tools import BaseTool
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ authors = ["Your Name <you@example.com>"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.10,<=3.13"
|
||||
crewai = { extras = ["tools"], version = ">=0.76.9,<1.0.0" }
|
||||
crewai = { extras = ["tools"], version = ">=0.80.0,<1.0.0" }
|
||||
asyncio = "*"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from typing import Type
|
||||
from crewai_tools import BaseTool
|
||||
from crewai.tools import BaseTool
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class MyCustomToolInput(BaseModel):
|
||||
"""Input schema for MyCustomTool."""
|
||||
argument: str = Field(..., description="Description of the argument.")
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
|
||||
authors = ["Your Name <you@example.com>"]
|
||||
requires-python = ">=3.10,<=3.13"
|
||||
dependencies = [
|
||||
"crewai[tools]>=0.76.9,<1.0.0"
|
||||
"crewai[tools]>=0.80.0,<1.0.0"
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from typing import Type
|
||||
from crewai_tools import BaseTool
|
||||
from crewai.tools import BaseTool
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class MyCustomToolInput(BaseModel):
|
||||
"""Input schema for MyCustomTool."""
|
||||
argument: str = Field(..., description="Description of the argument.")
|
||||
|
||||
@@ -5,6 +5,6 @@ description = "Power up your crews with {{folder_name}}"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10,<=3.13"
|
||||
dependencies = [
|
||||
"crewai[tools]>=0.76.9"
|
||||
"crewai[tools]>=0.80.0"
|
||||
]
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from crewai_tools import BaseTool
|
||||
from crewai.tools import BaseTool
|
||||
|
||||
|
||||
class {{class_name}}(BaseTool):
|
||||
name: str = "Name of my tool"
|
||||
|
||||
@@ -5,7 +5,7 @@ import uuid
|
||||
import warnings
|
||||
from concurrent.futures import Future
|
||||
from hashlib import md5
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
|
||||
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union
|
||||
|
||||
from pydantic import (
|
||||
UUID4,
|
||||
@@ -27,17 +27,17 @@ from crewai.llm import LLM
|
||||
from crewai.memory.entity.entity_memory import EntityMemory
|
||||
from crewai.memory.long_term.long_term_memory import LongTermMemory
|
||||
from crewai.memory.short_term.short_term_memory import ShortTermMemory
|
||||
from crewai.knowledge.knowledge import Knowledge
|
||||
from crewai.memory.user.user_memory import UserMemory
|
||||
from crewai.process import Process
|
||||
from crewai.task import Task
|
||||
from crewai.tasks.conditional_task import ConditionalTask
|
||||
from crewai.tasks.task_output import TaskOutput
|
||||
from crewai.telemetry import Telemetry
|
||||
from crewai.tools.agent_tools import AgentTools
|
||||
from crewai.tools.agent_tools.agent_tools import AgentTools
|
||||
from crewai.types.usage_metrics import UsageMetrics
|
||||
from crewai.utilities import I18N, FileHandler, Logger, RPMController
|
||||
from crewai.utilities.constants import (
|
||||
TRAINING_DATA_FILE,
|
||||
)
|
||||
from crewai.utilities.constants import TRAINING_DATA_FILE
|
||||
from crewai.utilities.evaluators.crew_evaluator_handler import CrewEvaluator
|
||||
from crewai.utilities.evaluators.task_evaluator import TaskEvaluator
|
||||
from crewai.utilities.formatter import (
|
||||
@@ -71,6 +71,7 @@ class Crew(BaseModel):
|
||||
manager_llm: The language model that will run manager agent.
|
||||
manager_agent: Custom agent that will be used as manager.
|
||||
memory: Whether the crew should use memory to store memories of it's execution.
|
||||
memory_config: Configuration for the memory to be used for the crew.
|
||||
cache: Whether the crew should use a cache to store the results of the tools execution.
|
||||
function_calling_llm: The language model that will run the tool calling for all the agents.
|
||||
process: The process flow that the crew will follow (e.g., sequential, hierarchical).
|
||||
@@ -94,6 +95,7 @@ class Crew(BaseModel):
|
||||
_short_term_memory: Optional[InstanceOf[ShortTermMemory]] = PrivateAttr()
|
||||
_long_term_memory: Optional[InstanceOf[LongTermMemory]] = PrivateAttr()
|
||||
_entity_memory: Optional[InstanceOf[EntityMemory]] = PrivateAttr()
|
||||
_user_memory: Optional[InstanceOf[UserMemory]] = PrivateAttr()
|
||||
_train: Optional[bool] = PrivateAttr(default=False)
|
||||
_train_iteration: Optional[int] = PrivateAttr()
|
||||
_inputs: Optional[Dict[str, Any]] = PrivateAttr(default=None)
|
||||
@@ -114,6 +116,10 @@ class Crew(BaseModel):
|
||||
default=False,
|
||||
description="Whether the crew should use memory to store memories of it's execution",
|
||||
)
|
||||
memory_config: Optional[Dict[str, Any]] = Field(
|
||||
default=None,
|
||||
description="Configuration for the memory to be used for the crew.",
|
||||
)
|
||||
short_term_memory: Optional[InstanceOf[ShortTermMemory]] = Field(
|
||||
default=None,
|
||||
description="An Instance of the ShortTermMemory to be used by the Crew",
|
||||
@@ -126,7 +132,11 @@ class Crew(BaseModel):
|
||||
default=None,
|
||||
description="An Instance of the EntityMemory to be used by the Crew",
|
||||
)
|
||||
embedder: Optional[Any] = Field(
|
||||
user_memory: Optional[InstanceOf[UserMemory]] = Field(
|
||||
default=None,
|
||||
description="An instance of the UserMemory to be used by the Crew to store/fetch memories of a specific user.",
|
||||
)
|
||||
embedder: Optional[dict] = Field(
|
||||
default=None,
|
||||
description="Configuration for the embedder to be used for the crew.",
|
||||
)
|
||||
@@ -154,6 +164,16 @@ class Crew(BaseModel):
|
||||
default=None,
|
||||
description="Callback to be executed after each task for all agents execution.",
|
||||
)
|
||||
before_kickoff_callbacks: List[
|
||||
Callable[[Optional[Dict[str, Any]]], Optional[Dict[str, Any]]]
|
||||
] = Field(
|
||||
default_factory=list,
|
||||
description="List of callbacks to be executed before crew kickoff. It may be used to adjust inputs before the crew is executed.",
|
||||
)
|
||||
after_kickoff_callbacks: List[Callable[[CrewOutput], CrewOutput]] = Field(
|
||||
default_factory=list,
|
||||
description="List of callbacks to be executed after crew kickoff. It may be used to adjust the output of the crew.",
|
||||
)
|
||||
max_rpm: Optional[int] = Field(
|
||||
default=None,
|
||||
description="Maximum number of requests per minute for the crew execution to be respected.",
|
||||
@@ -182,6 +202,10 @@ class Crew(BaseModel):
|
||||
default=[],
|
||||
description="List of execution logs for tasks",
|
||||
)
|
||||
knowledge: Optional[Dict[str, Any]] = Field(
|
||||
default=None, description="Knowledge for the crew. Add knowledge sources to the knowledge object."
|
||||
)
|
||||
|
||||
|
||||
@field_validator("id", mode="before")
|
||||
@classmethod
|
||||
@@ -238,13 +262,31 @@ class Crew(BaseModel):
|
||||
self._short_term_memory = (
|
||||
self.short_term_memory
|
||||
if self.short_term_memory
|
||||
else ShortTermMemory(crew=self, embedder_config=self.embedder)
|
||||
else ShortTermMemory(
|
||||
crew=self,
|
||||
embedder_config=self.embedder,
|
||||
)
|
||||
)
|
||||
self._entity_memory = (
|
||||
self.entity_memory
|
||||
if self.entity_memory
|
||||
else EntityMemory(crew=self, embedder_config=self.embedder)
|
||||
)
|
||||
if hasattr(self, "memory_config") and self.memory_config is not None:
|
||||
self._user_memory = (
|
||||
self.user_memory if self.user_memory else UserMemory(crew=self)
|
||||
)
|
||||
else:
|
||||
self._user_memory = None
|
||||
return self
|
||||
|
||||
@model_validator(mode="after")
|
||||
def create_crew_knowledge(self) -> "Crew":
|
||||
if self.knowledge:
|
||||
try:
|
||||
self.knowledge = Knowledge(**self.knowledge) if isinstance(self.knowledge, dict) else self.knowledge
|
||||
except (TypeError, ValueError) as e:
|
||||
raise ValueError(f"Invalid knowledge configuration: {str(e)}")
|
||||
return self
|
||||
|
||||
@model_validator(mode="after")
|
||||
@@ -445,18 +487,22 @@ class Crew(BaseModel):
|
||||
training_data = CrewTrainingHandler(TRAINING_DATA_FILE).load()
|
||||
|
||||
for agent in train_crew.agents:
|
||||
result = TaskEvaluator(agent).evaluate_training_data(
|
||||
training_data=training_data, agent_id=str(agent.id)
|
||||
)
|
||||
if training_data.get(str(agent.id)):
|
||||
result = TaskEvaluator(agent).evaluate_training_data(
|
||||
training_data=training_data, agent_id=str(agent.id)
|
||||
)
|
||||
|
||||
CrewTrainingHandler(filename).save_trained_data(
|
||||
agent_id=str(agent.role), trained_data=result.model_dump()
|
||||
)
|
||||
CrewTrainingHandler(filename).save_trained_data(
|
||||
agent_id=str(agent.role), trained_data=result.model_dump()
|
||||
)
|
||||
|
||||
def kickoff(
|
||||
self,
|
||||
inputs: Optional[Dict[str, Any]] = None,
|
||||
) -> CrewOutput:
|
||||
for before_callback in self.before_kickoff_callbacks:
|
||||
inputs = before_callback(inputs)
|
||||
|
||||
"""Starts the crew to work on its assigned tasks."""
|
||||
self._execution_span = self._telemetry.crew_execution_span(self, inputs)
|
||||
self._task_output_handler.reset()
|
||||
@@ -499,6 +545,9 @@ class Crew(BaseModel):
|
||||
f"The process '{self.process}' is not implemented yet."
|
||||
)
|
||||
|
||||
for after_callback in self.after_kickoff_callbacks:
|
||||
result = after_callback(result)
|
||||
|
||||
metrics += [agent._token_process.get_summary() for agent in self.agents]
|
||||
|
||||
self.usage_metrics = UsageMetrics()
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
import asyncio
|
||||
import inspect
|
||||
from typing import Any, Callable, Dict, Generic, List, Set, Type, TypeVar, Union
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Generic,
|
||||
List,
|
||||
Optional,
|
||||
Set,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, ValidationError
|
||||
|
||||
from crewai.flow.flow_visualizer import plot_flow
|
||||
from crewai.flow.utils import get_possible_return_constants
|
||||
@@ -119,7 +131,6 @@ class FlowMeta(type):
|
||||
condition_type = getattr(attr_value, "__condition_type__", "OR")
|
||||
listeners[attr_name] = (condition_type, methods)
|
||||
|
||||
# TODO: should we add a check for __condition_type__ 'AND'?
|
||||
elif hasattr(attr_value, "__is_router__"):
|
||||
routers[attr_value.__router_for__] = attr_name
|
||||
possible_returns = get_possible_return_constants(attr_value)
|
||||
@@ -159,8 +170,7 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
||||
def __init__(self) -> None:
|
||||
self._methods: Dict[str, Callable] = {}
|
||||
self._state: T = self._create_initial_state()
|
||||
self._executed_methods: Set[str] = set()
|
||||
self._scheduled_tasks: Set[str] = set()
|
||||
self._method_execution_counts: Dict[str, int] = {}
|
||||
self._pending_and_listeners: Dict[str, Set[str]] = {}
|
||||
self._method_outputs: List[Any] = [] # List to store all method outputs
|
||||
|
||||
@@ -191,10 +201,74 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
||||
"""Returns the list of all outputs from executed methods."""
|
||||
return self._method_outputs
|
||||
|
||||
def kickoff(self) -> Any:
|
||||
def _initialize_state(self, inputs: Dict[str, Any]) -> None:
|
||||
"""
|
||||
Initializes or updates the state with the provided inputs.
|
||||
|
||||
Args:
|
||||
inputs: Dictionary of inputs to initialize or update the state.
|
||||
|
||||
Raises:
|
||||
ValueError: If inputs do not match the structured state model.
|
||||
TypeError: If state is neither a BaseModel instance nor a dictionary.
|
||||
"""
|
||||
if isinstance(self._state, BaseModel):
|
||||
# Structured state management
|
||||
try:
|
||||
# Define a function to create the dynamic class
|
||||
def create_model_with_extra_forbid(
|
||||
base_model: Type[BaseModel],
|
||||
) -> Type[BaseModel]:
|
||||
class ModelWithExtraForbid(base_model): # type: ignore
|
||||
model_config = base_model.model_config.copy()
|
||||
model_config["extra"] = "forbid"
|
||||
|
||||
return ModelWithExtraForbid
|
||||
|
||||
# Create the dynamic class
|
||||
ModelWithExtraForbid = create_model_with_extra_forbid(
|
||||
self._state.__class__
|
||||
)
|
||||
|
||||
# Create a new instance using the combined state and inputs
|
||||
self._state = cast(
|
||||
T, ModelWithExtraForbid(**{**self._state.model_dump(), **inputs})
|
||||
)
|
||||
|
||||
except ValidationError as e:
|
||||
raise ValueError(f"Invalid inputs for structured state: {e}") from e
|
||||
elif isinstance(self._state, dict):
|
||||
# Unstructured state management
|
||||
self._state.update(inputs)
|
||||
else:
|
||||
raise TypeError("State must be a BaseModel instance or a dictionary.")
|
||||
|
||||
def kickoff(self, inputs: Optional[Dict[str, Any]] = None) -> Any:
|
||||
"""
|
||||
Starts the execution of the flow synchronously.
|
||||
|
||||
Args:
|
||||
inputs: Optional dictionary of inputs to initialize or update the state.
|
||||
|
||||
Returns:
|
||||
The final output from the flow execution.
|
||||
"""
|
||||
if inputs is not None:
|
||||
self._initialize_state(inputs)
|
||||
return asyncio.run(self.kickoff_async())
|
||||
|
||||
async def kickoff_async(self) -> Any:
|
||||
async def kickoff_async(self, inputs: Optional[Dict[str, Any]] = None) -> Any:
|
||||
"""
|
||||
Starts the execution of the flow asynchronously.
|
||||
|
||||
Args:
|
||||
inputs: Optional dictionary of inputs to initialize or update the state.
|
||||
|
||||
Returns:
|
||||
The final output from the flow execution.
|
||||
"""
|
||||
if inputs is not None:
|
||||
self._initialize_state(inputs)
|
||||
if not self._start_methods:
|
||||
raise ValueError("No start method defined")
|
||||
|
||||
@@ -233,7 +307,10 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
||||
)
|
||||
self._method_outputs.append(result) # Store the output
|
||||
|
||||
self._executed_methods.add(method_name)
|
||||
# Track method execution counts
|
||||
self._method_execution_counts[method_name] = (
|
||||
self._method_execution_counts.get(method_name, 0) + 1
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
@@ -243,35 +320,34 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
||||
if trigger_method in self._routers:
|
||||
router_method = self._methods[self._routers[trigger_method]]
|
||||
path = await self._execute_method(
|
||||
trigger_method, router_method
|
||||
) # TODO: Change or not?
|
||||
# Use the path as the new trigger method
|
||||
self._routers[trigger_method], router_method
|
||||
)
|
||||
trigger_method = path
|
||||
|
||||
for listener_name, (condition_type, methods) in self._listeners.items():
|
||||
if condition_type == "OR":
|
||||
if trigger_method in methods:
|
||||
if (
|
||||
listener_name not in self._executed_methods
|
||||
and listener_name not in self._scheduled_tasks
|
||||
):
|
||||
self._scheduled_tasks.add(listener_name)
|
||||
listener_tasks.append(
|
||||
self._execute_single_listener(listener_name, result)
|
||||
)
|
||||
# Schedule the listener without preventing re-execution
|
||||
listener_tasks.append(
|
||||
self._execute_single_listener(listener_name, result)
|
||||
)
|
||||
elif condition_type == "AND":
|
||||
if all(method in self._executed_methods for method in methods):
|
||||
if (
|
||||
listener_name not in self._executed_methods
|
||||
and listener_name not in self._scheduled_tasks
|
||||
):
|
||||
self._scheduled_tasks.add(listener_name)
|
||||
listener_tasks.append(
|
||||
self._execute_single_listener(listener_name, result)
|
||||
)
|
||||
# Initialize pending methods for this listener if not already done
|
||||
if listener_name not in self._pending_and_listeners:
|
||||
self._pending_and_listeners[listener_name] = set(methods)
|
||||
# Remove the trigger method from pending methods
|
||||
self._pending_and_listeners[listener_name].discard(trigger_method)
|
||||
if not self._pending_and_listeners[listener_name]:
|
||||
# All required methods have been executed
|
||||
listener_tasks.append(
|
||||
self._execute_single_listener(listener_name, result)
|
||||
)
|
||||
# Reset pending methods for this listener
|
||||
self._pending_and_listeners.pop(listener_name, None)
|
||||
|
||||
# Run all listener tasks concurrently and wait for them to complete
|
||||
await asyncio.gather(*listener_tasks)
|
||||
if listener_tasks:
|
||||
await asyncio.gather(*listener_tasks)
|
||||
|
||||
async def _execute_single_listener(self, listener_name: str, result: Any) -> None:
|
||||
try:
|
||||
@@ -291,9 +367,6 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
||||
# If listener does not expect parameters, call without arguments
|
||||
listener_result = await self._execute_method(listener_name, method)
|
||||
|
||||
# Remove from scheduled tasks after execution
|
||||
self._scheduled_tasks.discard(listener_name)
|
||||
|
||||
# Execute listeners of this listener
|
||||
await self._execute_listeners(listener_name, listener_result)
|
||||
except Exception as e:
|
||||
|
||||
55
src/crewai/knowledge/embedder/base_embedder.py
Normal file
55
src/crewai/knowledge/embedder/base_embedder.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
class BaseEmbedder(ABC):
|
||||
"""
|
||||
Abstract base class for text embedding models
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def embed_chunks(self, chunks: List[str]) -> np.ndarray:
|
||||
"""
|
||||
Generate embeddings for a list of text chunks
|
||||
|
||||
Args:
|
||||
chunks: List of text chunks to embed
|
||||
|
||||
Returns:
|
||||
Array of embeddings
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def embed_texts(self, texts: List[str]) -> np.ndarray:
|
||||
"""
|
||||
Generate embeddings for a list of texts
|
||||
|
||||
Args:
|
||||
texts: List of texts to embed
|
||||
|
||||
Returns:
|
||||
Array of embeddings
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def embed_text(self, text: str) -> np.ndarray:
|
||||
"""
|
||||
Generate embedding for a single text
|
||||
|
||||
Args:
|
||||
text: Text to embed
|
||||
|
||||
Returns:
|
||||
Embedding array
|
||||
"""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def dimension(self) -> int:
|
||||
"""Get the dimension of the embeddings"""
|
||||
pass
|
||||
93
src/crewai/knowledge/embedder/fastembed.py
Normal file
93
src/crewai/knowledge/embedder/fastembed.py
Normal file
@@ -0,0 +1,93 @@
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
from .base_embedder import BaseEmbedder
|
||||
|
||||
try:
|
||||
from fastembed_gpu import TextEmbedding # type: ignore
|
||||
|
||||
FASTEMBED_AVAILABLE = True
|
||||
except ImportError:
|
||||
try:
|
||||
from fastembed import TextEmbedding
|
||||
|
||||
FASTEMBED_AVAILABLE = True
|
||||
except ImportError:
|
||||
FASTEMBED_AVAILABLE = False
|
||||
|
||||
|
||||
class FastEmbed(BaseEmbedder):
|
||||
"""
|
||||
A wrapper class for text embedding models using FastEmbed
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
model_name: str = "BAAI/bge-small-en-v1.5",
|
||||
cache_dir: Optional[Union[str, Path]] = None,
|
||||
):
|
||||
"""
|
||||
Initialize the embedding model
|
||||
|
||||
Args:
|
||||
model_name: Name of the model to use
|
||||
cache_dir: Directory to cache the model
|
||||
gpu: Whether to use GPU acceleration
|
||||
"""
|
||||
if not FASTEMBED_AVAILABLE:
|
||||
raise ImportError(
|
||||
"FastEmbed is not installed. Please install it with: "
|
||||
"uv pip install fastembed or uv pip install fastembed-gpu for GPU support"
|
||||
)
|
||||
|
||||
self.model = TextEmbedding(
|
||||
model_name=model_name,
|
||||
cache_dir=str(cache_dir) if cache_dir else None,
|
||||
)
|
||||
|
||||
def embed_chunks(self, chunks: List[str]) -> List[np.ndarray]:
|
||||
"""
|
||||
Generate embeddings for a list of text chunks
|
||||
|
||||
Args:
|
||||
chunks: List of text chunks to embed
|
||||
|
||||
Returns:
|
||||
List of embeddings
|
||||
"""
|
||||
embeddings = list(self.model.embed(chunks))
|
||||
return embeddings
|
||||
|
||||
def embed_texts(self, texts: List[str]) -> List[np.ndarray]:
|
||||
"""
|
||||
Generate embeddings for a list of texts
|
||||
|
||||
Args:
|
||||
texts: List of texts to embed
|
||||
|
||||
Returns:
|
||||
List of embeddings
|
||||
"""
|
||||
embeddings = list(self.model.embed(texts))
|
||||
return embeddings
|
||||
|
||||
def embed_text(self, text: str) -> np.ndarray:
|
||||
"""
|
||||
Generate embedding for a single text
|
||||
|
||||
Args:
|
||||
text: Text to embed
|
||||
|
||||
Returns:
|
||||
Embedding array
|
||||
"""
|
||||
return self.embed_texts([text])[0]
|
||||
|
||||
@property
|
||||
def dimension(self) -> int:
|
||||
"""Get the dimension of the embeddings"""
|
||||
# Generate a test embedding to get dimensions
|
||||
test_embed = self.embed_text("test")
|
||||
return len(test_embed)
|
||||
54
src/crewai/knowledge/knowledge.py
Normal file
54
src/crewai/knowledge/knowledge.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import os
|
||||
|
||||
from typing import List, Optional, Dict, Any
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource
|
||||
from crewai.knowledge.storage.knowledge_storage import KnowledgeStorage
|
||||
from crewai.utilities.logger import Logger
|
||||
from crewai.utilities.constants import DEFAULT_SCORE_THRESHOLD
|
||||
os.environ["TOKENIZERS_PARALLELISM"] = "false" # removes logging from fastembed
|
||||
|
||||
|
||||
class Knowledge(BaseModel):
|
||||
"""
|
||||
Knowledge is a collection of sources and setup for the vector store to save and query relevant context.
|
||||
Args:
|
||||
sources: List[BaseKnowledgeSource] = Field(default_factory=list)
|
||||
storage: KnowledgeStorage = Field(default_factory=KnowledgeStorage)
|
||||
embedder_config: Optional[Dict[str, Any]] = None
|
||||
"""
|
||||
sources: List[BaseKnowledgeSource] = Field(default_factory=list)
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
storage: KnowledgeStorage = Field(default_factory=KnowledgeStorage)
|
||||
embedder_config: Optional[Dict[str, Any]] = None
|
||||
|
||||
def __init__(self, embedder_config: Optional[Dict[str, Any]] = None, **data):
|
||||
super().__init__(**data)
|
||||
self.storage = KnowledgeStorage(embedder_config=embedder_config or None)
|
||||
|
||||
try:
|
||||
for source in self.sources:
|
||||
source.add()
|
||||
except Exception as e:
|
||||
Logger(verbose=True).log(
|
||||
"warning",
|
||||
f"Failed to init knowledge: {e}",
|
||||
color="yellow",
|
||||
)
|
||||
|
||||
def query(
|
||||
self, query: List[str], limit: int = 3, preference: Optional[str] = None
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Query across all knowledge sources to find the most relevant information.
|
||||
Returns the top_k most relevant chunks.
|
||||
"""
|
||||
|
||||
results = self.storage.search(
|
||||
query,
|
||||
limit,
|
||||
filter={"preference": preference} if preference else None,
|
||||
score_threshold=DEFAULT_SCORE_THRESHOLD,
|
||||
)
|
||||
return results
|
||||
0
src/crewai/knowledge/source/__init__.py
Normal file
0
src/crewai/knowledge/source/__init__.py
Normal file
36
src/crewai/knowledge/source/base_file_knowledge_source.py
Normal file
36
src/crewai/knowledge/source/base_file_knowledge_source.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from pathlib import Path
|
||||
from typing import Union, List
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource
|
||||
from typing import Dict, Any
|
||||
from crewai.knowledge.storage.knowledge_storage import KnowledgeStorage
|
||||
|
||||
|
||||
class BaseFileKnowledgeSource(BaseKnowledgeSource):
|
||||
"""Base class for knowledge sources that load content from files."""
|
||||
|
||||
file_path: Union[Path, List[Path]] = Field(...)
|
||||
content: Dict[Path, str] = Field(init=False, default_factory=dict)
|
||||
storage: KnowledgeStorage = Field(default_factory=KnowledgeStorage)
|
||||
|
||||
def model_post_init(self, _):
|
||||
"""Post-initialization method to load content."""
|
||||
self.content = self.load_content()
|
||||
|
||||
def load_content(self) -> Dict[Path, str]:
|
||||
"""Load and preprocess file content. Should be overridden by subclasses."""
|
||||
paths = [self.file_path] if isinstance(self.file_path, Path) else self.file_path
|
||||
|
||||
for path in paths:
|
||||
if not path.exists():
|
||||
raise FileNotFoundError(f"File not found: {path}")
|
||||
if not path.is_file():
|
||||
raise ValueError(f"Path is not a file: {path}")
|
||||
return {}
|
||||
|
||||
def save_documents(self, metadata: Dict[str, Any]):
|
||||
"""Save the documents to the storage."""
|
||||
chunk_metadatas = [metadata.copy() for _ in self.chunks]
|
||||
self.storage.save(self.chunks, chunk_metadatas)
|
||||
48
src/crewai/knowledge/source/base_knowledge_source.py
Normal file
48
src/crewai/knowledge/source/base_knowledge_source.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List, Dict, Any
|
||||
|
||||
import numpy as np
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from crewai.knowledge.storage.knowledge_storage import KnowledgeStorage
|
||||
|
||||
|
||||
class BaseKnowledgeSource(BaseModel, ABC):
|
||||
"""Abstract base class for knowledge sources."""
|
||||
|
||||
chunk_size: int = 4000
|
||||
chunk_overlap: int = 200
|
||||
chunks: List[str] = Field(default_factory=list)
|
||||
chunk_embeddings: List[np.ndarray] = Field(default_factory=list)
|
||||
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
storage: KnowledgeStorage = Field(default_factory=KnowledgeStorage)
|
||||
metadata: Dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
@abstractmethod
|
||||
def load_content(self) -> Dict[Any, str]:
|
||||
"""Load and preprocess content from the source."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def add(self) -> None:
|
||||
"""Process content, chunk it, compute embeddings, and save them."""
|
||||
pass
|
||||
|
||||
def get_embeddings(self) -> List[np.ndarray]:
|
||||
"""Return the list of embeddings for the chunks."""
|
||||
return self.chunk_embeddings
|
||||
|
||||
def _chunk_text(self, text: str) -> List[str]:
|
||||
"""Utility method to split text into chunks."""
|
||||
return [
|
||||
text[i : i + self.chunk_size]
|
||||
for i in range(0, len(text), self.chunk_size - self.chunk_overlap)
|
||||
]
|
||||
|
||||
def save_documents(self, metadata: Dict[str, Any]):
|
||||
"""
|
||||
Save the documents to the storage.
|
||||
This method should be called after the chunks and embeddings are generated.
|
||||
"""
|
||||
self.storage.save(self.chunks, metadata)
|
||||
44
src/crewai/knowledge/source/csv_knowledge_source.py
Normal file
44
src/crewai/knowledge/source/csv_knowledge_source.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import csv
|
||||
from typing import Dict, List
|
||||
from pathlib import Path
|
||||
|
||||
from crewai.knowledge.source.base_file_knowledge_source import BaseFileKnowledgeSource
|
||||
|
||||
|
||||
class CSVKnowledgeSource(BaseFileKnowledgeSource):
|
||||
"""A knowledge source that stores and queries CSV file content using embeddings."""
|
||||
|
||||
def load_content(self) -> Dict[Path, str]:
|
||||
"""Load and preprocess CSV file content."""
|
||||
super().load_content() # Validate the file path
|
||||
|
||||
file_path = (
|
||||
self.file_path[0] if isinstance(self.file_path, list) else self.file_path
|
||||
)
|
||||
file_path = Path(file_path) if isinstance(file_path, str) else file_path
|
||||
|
||||
with open(file_path, "r", encoding="utf-8") as csvfile:
|
||||
reader = csv.reader(csvfile)
|
||||
content = ""
|
||||
for row in reader:
|
||||
content += " ".join(row) + "\n"
|
||||
return {file_path: content}
|
||||
|
||||
def add(self) -> None:
|
||||
"""
|
||||
Add CSV file content to the knowledge source, chunk it, compute embeddings,
|
||||
and save the embeddings.
|
||||
"""
|
||||
content_str = (
|
||||
str(self.content) if isinstance(self.content, dict) else self.content
|
||||
)
|
||||
new_chunks = self._chunk_text(content_str)
|
||||
self.chunks.extend(new_chunks)
|
||||
self.save_documents(metadata=self.metadata)
|
||||
|
||||
def _chunk_text(self, text: str) -> List[str]:
|
||||
"""Utility method to split text into chunks."""
|
||||
return [
|
||||
text[i : i + self.chunk_size]
|
||||
for i in range(0, len(text), self.chunk_size - self.chunk_overlap)
|
||||
]
|
||||
56
src/crewai/knowledge/source/excel_knowledge_source.py
Normal file
56
src/crewai/knowledge/source/excel_knowledge_source.py
Normal file
@@ -0,0 +1,56 @@
|
||||
from typing import Dict, List
|
||||
from pathlib import Path
|
||||
from crewai.knowledge.source.base_file_knowledge_source import BaseFileKnowledgeSource
|
||||
|
||||
|
||||
class ExcelKnowledgeSource(BaseFileKnowledgeSource):
|
||||
"""A knowledge source that stores and queries Excel file content using embeddings."""
|
||||
|
||||
def load_content(self) -> Dict[Path, str]:
|
||||
"""Load and preprocess Excel file content."""
|
||||
super().load_content() # Validate the file path
|
||||
pd = self._import_dependencies()
|
||||
|
||||
if isinstance(self.file_path, list):
|
||||
file_path = self.file_path[0]
|
||||
else:
|
||||
file_path = self.file_path
|
||||
|
||||
df = pd.read_excel(file_path)
|
||||
content = df.to_csv(index=False)
|
||||
return {file_path: content}
|
||||
|
||||
def _import_dependencies(self):
|
||||
"""Dynamically import dependencies."""
|
||||
try:
|
||||
import openpyxl # noqa
|
||||
import pandas as pd
|
||||
|
||||
return pd
|
||||
except ImportError as e:
|
||||
missing_package = str(e).split()[-1]
|
||||
raise ImportError(
|
||||
f"{missing_package} is not installed. Please install it with: pip install {missing_package}"
|
||||
)
|
||||
|
||||
def add(self) -> None:
|
||||
"""
|
||||
Add Excel file content to the knowledge source, chunk it, compute embeddings,
|
||||
and save the embeddings.
|
||||
"""
|
||||
# Convert dictionary values to a single string if content is a dictionary
|
||||
if isinstance(self.content, dict):
|
||||
content_str = "\n".join(str(value) for value in self.content.values())
|
||||
else:
|
||||
content_str = str(self.content)
|
||||
|
||||
new_chunks = self._chunk_text(content_str)
|
||||
self.chunks.extend(new_chunks)
|
||||
self.save_documents(metadata=self.metadata)
|
||||
|
||||
def _chunk_text(self, text: str) -> List[str]:
|
||||
"""Utility method to split text into chunks."""
|
||||
return [
|
||||
text[i : i + self.chunk_size]
|
||||
for i in range(0, len(text), self.chunk_size - self.chunk_overlap)
|
||||
]
|
||||
54
src/crewai/knowledge/source/json_knowledge_source.py
Normal file
54
src/crewai/knowledge/source/json_knowledge_source.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import json
|
||||
from typing import Any, Dict, List
|
||||
from pathlib import Path
|
||||
|
||||
from crewai.knowledge.source.base_file_knowledge_source import BaseFileKnowledgeSource
|
||||
|
||||
|
||||
class JSONKnowledgeSource(BaseFileKnowledgeSource):
|
||||
"""A knowledge source that stores and queries JSON file content using embeddings."""
|
||||
|
||||
def load_content(self) -> Dict[Path, str]:
|
||||
"""Load and preprocess JSON file content."""
|
||||
super().load_content() # Validate the file path
|
||||
paths = [self.file_path] if isinstance(self.file_path, Path) else self.file_path
|
||||
|
||||
content: Dict[Path, str] = {}
|
||||
for path in paths:
|
||||
with open(path, "r", encoding="utf-8") as json_file:
|
||||
data = json.load(json_file)
|
||||
content[path] = self._json_to_text(data)
|
||||
return content
|
||||
|
||||
def _json_to_text(self, data: Any, level: int = 0) -> str:
|
||||
"""Recursively convert JSON data to a text representation."""
|
||||
text = ""
|
||||
indent = " " * level
|
||||
if isinstance(data, dict):
|
||||
for key, value in data.items():
|
||||
text += f"{indent}{key}: {self._json_to_text(value, level + 1)}\n"
|
||||
elif isinstance(data, list):
|
||||
for item in data:
|
||||
text += f"{indent}- {self._json_to_text(item, level + 1)}\n"
|
||||
else:
|
||||
text += f"{str(data)}"
|
||||
return text
|
||||
|
||||
def add(self) -> None:
|
||||
"""
|
||||
Add JSON file content to the knowledge source, chunk it, compute embeddings,
|
||||
and save the embeddings.
|
||||
"""
|
||||
content_str = (
|
||||
str(self.content) if isinstance(self.content, dict) else self.content
|
||||
)
|
||||
new_chunks = self._chunk_text(content_str)
|
||||
self.chunks.extend(new_chunks)
|
||||
self.save_documents(metadata=self.metadata)
|
||||
|
||||
def _chunk_text(self, text: str) -> List[str]:
|
||||
"""Utility method to split text into chunks."""
|
||||
return [
|
||||
text[i : i + self.chunk_size]
|
||||
for i in range(0, len(text), self.chunk_size - self.chunk_overlap)
|
||||
]
|
||||
54
src/crewai/knowledge/source/pdf_knowledge_source.py
Normal file
54
src/crewai/knowledge/source/pdf_knowledge_source.py
Normal file
@@ -0,0 +1,54 @@
|
||||
from typing import List, Dict
|
||||
from pathlib import Path
|
||||
|
||||
from crewai.knowledge.source.base_file_knowledge_source import BaseFileKnowledgeSource
|
||||
|
||||
|
||||
class PDFKnowledgeSource(BaseFileKnowledgeSource):
|
||||
"""A knowledge source that stores and queries PDF file content using embeddings."""
|
||||
|
||||
def load_content(self) -> Dict[Path, str]:
|
||||
"""Load and preprocess PDF file content."""
|
||||
super().load_content() # Validate the file paths
|
||||
pdfplumber = self._import_pdfplumber()
|
||||
|
||||
paths = [self.file_path] if isinstance(self.file_path, Path) else self.file_path
|
||||
content = {}
|
||||
|
||||
for path in paths:
|
||||
text = ""
|
||||
with pdfplumber.open(path) as pdf:
|
||||
for page in pdf.pages:
|
||||
page_text = page.extract_text()
|
||||
if page_text:
|
||||
text += page_text + "\n"
|
||||
content[path] = text
|
||||
return content
|
||||
|
||||
def _import_pdfplumber(self):
|
||||
"""Dynamically import pdfplumber."""
|
||||
try:
|
||||
import pdfplumber
|
||||
|
||||
return pdfplumber
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"pdfplumber is not installed. Please install it with: pip install pdfplumber"
|
||||
)
|
||||
|
||||
def add(self) -> None:
|
||||
"""
|
||||
Add PDF file content to the knowledge source, chunk it, compute embeddings,
|
||||
and save the embeddings.
|
||||
"""
|
||||
for _, text in self.content.items():
|
||||
new_chunks = self._chunk_text(text)
|
||||
self.chunks.extend(new_chunks)
|
||||
self.save_documents(metadata=self.metadata)
|
||||
|
||||
def _chunk_text(self, text: str) -> List[str]:
|
||||
"""Utility method to split text into chunks."""
|
||||
return [
|
||||
text[i : i + self.chunk_size]
|
||||
for i in range(0, len(text), self.chunk_size - self.chunk_overlap)
|
||||
]
|
||||
33
src/crewai/knowledge/source/string_knowledge_source.py
Normal file
33
src/crewai/knowledge/source/string_knowledge_source.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from typing import List
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource
|
||||
|
||||
|
||||
class StringKnowledgeSource(BaseKnowledgeSource):
|
||||
"""A knowledge source that stores and queries plain text content using embeddings."""
|
||||
|
||||
content: str = Field(...)
|
||||
|
||||
def model_post_init(self, _):
|
||||
"""Post-initialization method to validate content."""
|
||||
self.load_content()
|
||||
|
||||
def load_content(self):
|
||||
"""Validate string content."""
|
||||
if not isinstance(self.content, str):
|
||||
raise ValueError("StringKnowledgeSource only accepts string content")
|
||||
|
||||
def add(self) -> None:
|
||||
"""Add string content to the knowledge source, chunk it, compute embeddings, and save them."""
|
||||
new_chunks = self._chunk_text(self.content)
|
||||
self.chunks.extend(new_chunks)
|
||||
self.save_documents(metadata=self.metadata)
|
||||
|
||||
def _chunk_text(self, text: str) -> List[str]:
|
||||
"""Utility method to split text into chunks."""
|
||||
return [
|
||||
text[i : i + self.chunk_size]
|
||||
for i in range(0, len(text), self.chunk_size - self.chunk_overlap)
|
||||
]
|
||||
35
src/crewai/knowledge/source/text_file_knowledge_source.py
Normal file
35
src/crewai/knowledge/source/text_file_knowledge_source.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from typing import Dict, List
|
||||
from pathlib import Path
|
||||
|
||||
from crewai.knowledge.source.base_file_knowledge_source import BaseFileKnowledgeSource
|
||||
|
||||
|
||||
class TextFileKnowledgeSource(BaseFileKnowledgeSource):
|
||||
"""A knowledge source that stores and queries text file content using embeddings."""
|
||||
|
||||
def load_content(self) -> Dict[Path, str]:
|
||||
"""Load and preprocess text file content."""
|
||||
super().load_content()
|
||||
paths = [self.file_path] if isinstance(self.file_path, Path) else self.file_path
|
||||
content = {}
|
||||
for path in paths:
|
||||
with path.open("r", encoding="utf-8") as f:
|
||||
content[path] = f.read() # type: ignore
|
||||
return content
|
||||
|
||||
def add(self) -> None:
|
||||
"""
|
||||
Add text file content to the knowledge source, chunk it, compute embeddings,
|
||||
and save the embeddings.
|
||||
"""
|
||||
for _, text in self.content.items():
|
||||
new_chunks = self._chunk_text(text)
|
||||
self.chunks.extend(new_chunks)
|
||||
self.save_documents(metadata=self.metadata)
|
||||
|
||||
def _chunk_text(self, text: str) -> List[str]:
|
||||
"""Utility method to split text into chunks."""
|
||||
return [
|
||||
text[i : i + self.chunk_size]
|
||||
for i in range(0, len(text), self.chunk_size - self.chunk_overlap)
|
||||
]
|
||||
0
src/crewai/knowledge/storage/__init__.py
Normal file
0
src/crewai/knowledge/storage/__init__.py
Normal file
29
src/crewai/knowledge/storage/base_knowledge_storage.py
Normal file
29
src/crewai/knowledge/storage/base_knowledge_storage.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Dict, Any, List, Optional
|
||||
|
||||
|
||||
class BaseKnowledgeStorage(ABC):
|
||||
"""Abstract base class for knowledge storage implementations."""
|
||||
|
||||
@abstractmethod
|
||||
def search(
|
||||
self,
|
||||
query: List[str],
|
||||
limit: int = 3,
|
||||
filter: Optional[dict] = None,
|
||||
score_threshold: float = 0.35,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Search for documents in the knowledge base."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def save(
|
||||
self, documents: List[str], metadata: Dict[str, Any] | List[Dict[str, Any]]
|
||||
) -> None:
|
||||
"""Save documents to the knowledge base."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def reset(self) -> None:
|
||||
"""Reset the knowledge base."""
|
||||
pass
|
||||
132
src/crewai/knowledge/storage/knowledge_storage.py
Normal file
132
src/crewai/knowledge/storage/knowledge_storage.py
Normal file
@@ -0,0 +1,132 @@
|
||||
import contextlib
|
||||
import io
|
||||
import logging
|
||||
import chromadb
|
||||
import os
|
||||
from crewai.utilities.paths import db_storage_path
|
||||
from typing import Optional, List
|
||||
from typing import Dict, Any
|
||||
from crewai.utilities import EmbeddingConfigurator
|
||||
from crewai.knowledge.storage.base_knowledge_storage import BaseKnowledgeStorage
|
||||
import hashlib
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def suppress_logging(
|
||||
logger_name="chromadb.segment.impl.vector.local_persistent_hnsw",
|
||||
level=logging.ERROR,
|
||||
):
|
||||
logger = logging.getLogger(logger_name)
|
||||
original_level = logger.getEffectiveLevel()
|
||||
logger.setLevel(level)
|
||||
with (
|
||||
contextlib.redirect_stdout(io.StringIO()),
|
||||
contextlib.redirect_stderr(io.StringIO()),
|
||||
contextlib.suppress(UserWarning),
|
||||
):
|
||||
yield
|
||||
logger.setLevel(original_level)
|
||||
|
||||
|
||||
class KnowledgeStorage(BaseKnowledgeStorage):
|
||||
"""
|
||||
Extends Storage to handle embeddings for memory entries, improving
|
||||
search efficiency.
|
||||
"""
|
||||
|
||||
collection: Optional[chromadb.Collection] = None
|
||||
|
||||
def __init__(self, embedder_config: Optional[Dict[str, Any]] = None):
|
||||
self._initialize_app(embedder_config or {})
|
||||
|
||||
def search(
|
||||
self,
|
||||
query: List[str],
|
||||
limit: int = 3,
|
||||
filter: Optional[dict] = None,
|
||||
score_threshold: float = 0.35,
|
||||
) -> List[Dict[str, Any]]:
|
||||
with suppress_logging():
|
||||
if self.collection:
|
||||
fetched = self.collection.query(
|
||||
query_texts=query,
|
||||
n_results=limit,
|
||||
where=filter,
|
||||
)
|
||||
results = []
|
||||
for i in range(len(fetched["ids"][0])): # type: ignore
|
||||
result = {
|
||||
"id": fetched["ids"][0][i], # type: ignore
|
||||
"metadata": fetched["metadatas"][0][i], # type: ignore
|
||||
"context": fetched["documents"][0][i], # type: ignore
|
||||
"score": fetched["distances"][0][i], # type: ignore
|
||||
}
|
||||
if result["score"] >= score_threshold: # type: ignore
|
||||
results.append(result)
|
||||
return results
|
||||
else:
|
||||
raise Exception("Collection not initialized")
|
||||
|
||||
def _initialize_app(self, embedder_config: Optional[Dict[str, Any]] = None):
|
||||
import chromadb
|
||||
from chromadb.config import Settings
|
||||
|
||||
self._set_embedder_config(embedder_config)
|
||||
|
||||
chroma_client = chromadb.PersistentClient(
|
||||
path=f"{db_storage_path()}/knowledge",
|
||||
settings=Settings(allow_reset=True),
|
||||
)
|
||||
|
||||
self.app = chroma_client
|
||||
|
||||
try:
|
||||
self.collection = self.app.get_or_create_collection(name="knowledge")
|
||||
except Exception:
|
||||
raise Exception("Failed to create or get collection")
|
||||
|
||||
def reset(self):
|
||||
if self.app:
|
||||
self.app.reset()
|
||||
|
||||
def save(
|
||||
self, documents: List[str], metadata: Dict[str, Any] | List[Dict[str, Any]]
|
||||
):
|
||||
if self.collection:
|
||||
metadatas = [metadata] if isinstance(metadata, dict) else metadata
|
||||
|
||||
ids = [
|
||||
hashlib.sha256(doc.encode("utf-8")).hexdigest() for doc in documents
|
||||
]
|
||||
|
||||
self.collection.upsert(
|
||||
documents=documents,
|
||||
metadatas=metadatas,
|
||||
ids=ids,
|
||||
)
|
||||
else:
|
||||
raise Exception("Collection not initialized")
|
||||
|
||||
def _create_default_embedding_function(self):
|
||||
from chromadb.utils.embedding_functions.openai_embedding_function import (
|
||||
OpenAIEmbeddingFunction,
|
||||
)
|
||||
|
||||
return OpenAIEmbeddingFunction(
|
||||
api_key=os.getenv("OPENAI_API_KEY"), model_name="text-embedding-3-small"
|
||||
)
|
||||
|
||||
def _set_embedder_config(
|
||||
self, embedder_config: Optional[Dict[str, Any]] = None
|
||||
) -> None:
|
||||
"""Set the embedding configuration for the knowledge storage.
|
||||
|
||||
Args:
|
||||
embedder_config (Optional[Dict[str, Any]]): Configuration dictionary for the embedder.
|
||||
If None or empty, defaults to the default embedding function.
|
||||
"""
|
||||
self.embedder_config = (
|
||||
EmbeddingConfigurator().configure_embedder(embedder_config)
|
||||
if embedder_config
|
||||
else self._create_default_embedding_function()
|
||||
)
|
||||
@@ -1,7 +1,10 @@
|
||||
import io
|
||||
import logging
|
||||
import sys
|
||||
import warnings
|
||||
from contextlib import contextmanager
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
import logging
|
||||
import warnings
|
||||
|
||||
import litellm
|
||||
from litellm import get_supported_openai_params
|
||||
|
||||
@@ -9,9 +12,6 @@ from crewai.utilities.exceptions.context_window_exceeding_exception import (
|
||||
LLMContextLengthExceededException,
|
||||
)
|
||||
|
||||
import sys
|
||||
import io
|
||||
|
||||
|
||||
class FilteredStream(io.StringIO):
|
||||
def write(self, s):
|
||||
@@ -118,12 +118,12 @@ class LLM:
|
||||
|
||||
litellm.drop_params = True
|
||||
litellm.set_verbose = False
|
||||
litellm.callbacks = callbacks
|
||||
self.set_callbacks(callbacks)
|
||||
|
||||
def call(self, messages: List[Dict[str, str]], callbacks: List[Any] = []) -> str:
|
||||
with suppress_warnings():
|
||||
if callbacks and len(callbacks) > 0:
|
||||
litellm.callbacks = callbacks
|
||||
self.set_callbacks(callbacks)
|
||||
|
||||
try:
|
||||
params = {
|
||||
@@ -181,3 +181,15 @@ class LLM:
|
||||
def get_context_window_size(self) -> int:
|
||||
# Only using 75% of the context window size to avoid cutting the message in the middle
|
||||
return int(LLM_CONTEXT_WINDOW_SIZES.get(self.model, 8192) * 0.75)
|
||||
|
||||
def set_callbacks(self, callbacks: List[Any]):
|
||||
callback_types = [type(callback) for callback in callbacks]
|
||||
for callback in litellm.success_callback[:]:
|
||||
if type(callback) in callback_types:
|
||||
litellm.success_callback.remove(callback)
|
||||
|
||||
for callback in litellm._async_success_callback[:]:
|
||||
if type(callback) in callback_types:
|
||||
litellm._async_success_callback.remove(callback)
|
||||
|
||||
litellm.callbacks = callbacks
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from .entity.entity_memory import EntityMemory
|
||||
from .long_term.long_term_memory import LongTermMemory
|
||||
from .short_term.short_term_memory import ShortTermMemory
|
||||
from .user.user_memory import UserMemory
|
||||
|
||||
__all__ = ["EntityMemory", "LongTermMemory", "ShortTermMemory"]
|
||||
__all__ = ["UserMemory", "EntityMemory", "LongTermMemory", "ShortTermMemory"]
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
from typing import Optional
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
from crewai.memory import EntityMemory, LongTermMemory, ShortTermMemory
|
||||
from crewai.memory import EntityMemory, LongTermMemory, ShortTermMemory, UserMemory
|
||||
|
||||
|
||||
class ContextualMemory:
|
||||
def __init__(self, stm: ShortTermMemory, ltm: LongTermMemory, em: EntityMemory):
|
||||
def __init__(
|
||||
self,
|
||||
memory_config: Optional[Dict[str, Any]],
|
||||
stm: ShortTermMemory,
|
||||
ltm: LongTermMemory,
|
||||
em: EntityMemory,
|
||||
um: UserMemory,
|
||||
):
|
||||
if memory_config is not None:
|
||||
self.memory_provider = memory_config.get("provider")
|
||||
else:
|
||||
self.memory_provider = None
|
||||
self.stm = stm
|
||||
self.ltm = ltm
|
||||
self.em = em
|
||||
self.um = um
|
||||
|
||||
def build_context_for_task(self, task, context) -> str:
|
||||
"""
|
||||
@@ -23,6 +35,8 @@ class ContextualMemory:
|
||||
context.append(self._fetch_ltm_context(task.description))
|
||||
context.append(self._fetch_stm_context(query))
|
||||
context.append(self._fetch_entity_context(query))
|
||||
if self.memory_provider == "mem0":
|
||||
context.append(self._fetch_user_context(query))
|
||||
return "\n".join(filter(None, context))
|
||||
|
||||
def _fetch_stm_context(self, query) -> str:
|
||||
@@ -32,7 +46,10 @@ class ContextualMemory:
|
||||
"""
|
||||
stm_results = self.stm.search(query)
|
||||
formatted_results = "\n".join(
|
||||
[f"- {result['context']}" for result in stm_results]
|
||||
[
|
||||
f"- {result['memory'] if self.memory_provider == 'mem0' else result['context']}"
|
||||
for result in stm_results
|
||||
]
|
||||
)
|
||||
return f"Recent Insights:\n{formatted_results}" if stm_results else ""
|
||||
|
||||
@@ -62,6 +79,26 @@ class ContextualMemory:
|
||||
"""
|
||||
em_results = self.em.search(query)
|
||||
formatted_results = "\n".join(
|
||||
[f"- {result['context']}" for result in em_results] # type: ignore # Invalid index type "str" for "str"; expected type "SupportsIndex | slice"
|
||||
[
|
||||
f"- {result['memory'] if self.memory_provider == 'mem0' else result['context']}"
|
||||
for result in em_results
|
||||
] # type: ignore # Invalid index type "str" for "str"; expected type "SupportsIndex | slice"
|
||||
)
|
||||
return f"Entities:\n{formatted_results}" if em_results else ""
|
||||
|
||||
def _fetch_user_context(self, query: str) -> str:
|
||||
"""
|
||||
Fetches and formats relevant user information from User Memory.
|
||||
Args:
|
||||
query (str): The search query to find relevant user memories.
|
||||
Returns:
|
||||
str: Formatted user memories as bullet points, or an empty string if none found.
|
||||
"""
|
||||
user_memories = self.um.search(query)
|
||||
if not user_memories:
|
||||
return ""
|
||||
|
||||
formatted_memories = "\n".join(
|
||||
f"- {result['memory']}" for result in user_memories
|
||||
)
|
||||
return f"User memories/preferences:\n{formatted_memories}"
|
||||
|
||||
@@ -11,21 +11,43 @@ class EntityMemory(Memory):
|
||||
"""
|
||||
|
||||
def __init__(self, crew=None, embedder_config=None, storage=None):
|
||||
storage = (
|
||||
storage
|
||||
if storage
|
||||
else RAGStorage(
|
||||
type="entities",
|
||||
allow_reset=True,
|
||||
embedder_config=embedder_config,
|
||||
crew=crew,
|
||||
if hasattr(crew, "memory_config") and crew.memory_config is not None:
|
||||
self.memory_provider = crew.memory_config.get("provider")
|
||||
else:
|
||||
self.memory_provider = None
|
||||
|
||||
if self.memory_provider == "mem0":
|
||||
try:
|
||||
from crewai.memory.storage.mem0_storage import Mem0Storage
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Mem0 is not installed. Please install it with `pip install mem0ai`."
|
||||
)
|
||||
storage = Mem0Storage(type="entities", crew=crew)
|
||||
else:
|
||||
storage = (
|
||||
storage
|
||||
if storage
|
||||
else RAGStorage(
|
||||
type="entities",
|
||||
allow_reset=True,
|
||||
embedder_config=embedder_config,
|
||||
crew=crew,
|
||||
)
|
||||
)
|
||||
)
|
||||
super().__init__(storage)
|
||||
|
||||
def save(self, item: EntityMemoryItem) -> None: # type: ignore # BUG?: Signature of "save" incompatible with supertype "Memory"
|
||||
"""Saves an entity item into the SQLite storage."""
|
||||
data = f"{item.name}({item.type}): {item.description}"
|
||||
if self.memory_provider == "mem0":
|
||||
data = f"""
|
||||
Remember details about the following entity:
|
||||
Name: {item.name}
|
||||
Type: {item.type}
|
||||
Entity Description: {item.description}
|
||||
"""
|
||||
else:
|
||||
data = f"{item.name}({item.type}): {item.description}"
|
||||
super().save(data, item.metadata)
|
||||
|
||||
def reset(self) -> None:
|
||||
|
||||
@@ -23,5 +23,12 @@ class Memory:
|
||||
|
||||
self.storage.save(value, metadata)
|
||||
|
||||
def search(self, query: str) -> List[Dict[str, Any]]:
|
||||
return self.storage.search(query)
|
||||
def search(
|
||||
self,
|
||||
query: str,
|
||||
limit: int = 3,
|
||||
score_threshold: float = 0.35,
|
||||
) -> List[Any]:
|
||||
return self.storage.search(
|
||||
query=query, limit=limit, score_threshold=score_threshold
|
||||
)
|
||||
|
||||
@@ -14,13 +14,27 @@ class ShortTermMemory(Memory):
|
||||
"""
|
||||
|
||||
def __init__(self, crew=None, embedder_config=None, storage=None):
|
||||
storage = (
|
||||
storage
|
||||
if storage
|
||||
else RAGStorage(
|
||||
type="short_term", embedder_config=embedder_config, crew=crew
|
||||
if hasattr(crew, "memory_config") and crew.memory_config is not None:
|
||||
self.memory_provider = crew.memory_config.get("provider")
|
||||
else:
|
||||
self.memory_provider = None
|
||||
|
||||
if self.memory_provider == "mem0":
|
||||
try:
|
||||
from crewai.memory.storage.mem0_storage import Mem0Storage
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Mem0 is not installed. Please install it with `pip install mem0ai`."
|
||||
)
|
||||
storage = Mem0Storage(type="short_term", crew=crew)
|
||||
else:
|
||||
storage = (
|
||||
storage
|
||||
if storage
|
||||
else RAGStorage(
|
||||
type="short_term", embedder_config=embedder_config, crew=crew
|
||||
)
|
||||
)
|
||||
)
|
||||
super().__init__(storage)
|
||||
|
||||
def save(
|
||||
@@ -30,11 +44,20 @@ class ShortTermMemory(Memory):
|
||||
agent: Optional[str] = None,
|
||||
) -> None:
|
||||
item = ShortTermMemoryItem(data=value, metadata=metadata, agent=agent)
|
||||
if self.memory_provider == "mem0":
|
||||
item.data = f"Remember the following insights from Agent run: {item.data}"
|
||||
|
||||
super().save(value=item.data, metadata=item.metadata, agent=item.agent)
|
||||
|
||||
def search(self, query: str, score_threshold: float = 0.35):
|
||||
return self.storage.search(query=query, score_threshold=score_threshold) # type: ignore # BUG? The reference is to the parent class, but the parent class does not have this parameters
|
||||
def search(
|
||||
self,
|
||||
query: str,
|
||||
limit: int = 3,
|
||||
score_threshold: float = 0.35,
|
||||
):
|
||||
return self.storage.search(
|
||||
query=query, limit=limit, score_threshold=score_threshold
|
||||
) # type: ignore # BUG? The reference is to the parent class, but the parent class does not have this parameters
|
||||
|
||||
def reset(self) -> None:
|
||||
try:
|
||||
|
||||
@@ -7,8 +7,10 @@ class Storage:
|
||||
def save(self, value: Any, metadata: Dict[str, Any]) -> None:
|
||||
pass
|
||||
|
||||
def search(self, key: str) -> List[Dict[str, Any]]: # type: ignore
|
||||
pass
|
||||
def search(
|
||||
self, query: str, limit: int, score_threshold: float
|
||||
) -> Dict[str, Any] | List[Any]:
|
||||
return {}
|
||||
|
||||
def reset(self) -> None:
|
||||
pass
|
||||
|
||||
@@ -103,7 +103,7 @@ class KickoffTaskOutputsSQLiteStorage:
|
||||
else value
|
||||
)
|
||||
|
||||
query = f"UPDATE latest_kickoff_task_outputs SET {', '.join(fields)} WHERE task_index = ?"
|
||||
query = f"UPDATE latest_kickoff_task_outputs SET {', '.join(fields)} WHERE task_index = ?" # nosec
|
||||
values.append(task_index)
|
||||
|
||||
cursor.execute(query, tuple(values))
|
||||
|
||||
@@ -83,7 +83,7 @@ class LTMSQLiteStorage:
|
||||
WHERE task_description = ?
|
||||
ORDER BY datetime DESC, score ASC
|
||||
LIMIT {latest_n}
|
||||
""",
|
||||
""", # nosec
|
||||
(task_description,),
|
||||
)
|
||||
rows = cursor.fetchall()
|
||||
|
||||
104
src/crewai/memory/storage/mem0_storage.py
Normal file
104
src/crewai/memory/storage/mem0_storage.py
Normal file
@@ -0,0 +1,104 @@
|
||||
import os
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from mem0 import MemoryClient
|
||||
from crewai.memory.storage.interface import Storage
|
||||
|
||||
|
||||
class Mem0Storage(Storage):
|
||||
"""
|
||||
Extends Storage to handle embedding and searching across entities using Mem0.
|
||||
"""
|
||||
|
||||
def __init__(self, type, crew=None):
|
||||
super().__init__()
|
||||
|
||||
if type not in ["user", "short_term", "long_term", "entities"]:
|
||||
raise ValueError("Invalid type for Mem0Storage. Must be 'user' or 'agent'.")
|
||||
|
||||
self.memory_type = type
|
||||
self.crew = crew
|
||||
self.memory_config = crew.memory_config
|
||||
|
||||
# User ID is required for user memory type "user" since it's used as a unique identifier for the user.
|
||||
user_id = self._get_user_id()
|
||||
if type == "user" and not user_id:
|
||||
raise ValueError("User ID is required for user memory type")
|
||||
|
||||
# API key in memory config overrides the environment variable
|
||||
mem0_api_key = self.memory_config.get("config", {}).get("api_key") or os.getenv(
|
||||
"MEM0_API_KEY"
|
||||
)
|
||||
self.memory = MemoryClient(api_key=mem0_api_key)
|
||||
|
||||
def _sanitize_role(self, role: str) -> str:
|
||||
"""
|
||||
Sanitizes agent roles to ensure valid directory names.
|
||||
"""
|
||||
return role.replace("\n", "").replace(" ", "_").replace("/", "_")
|
||||
|
||||
def save(self, value: Any, metadata: Dict[str, Any]) -> None:
|
||||
user_id = self._get_user_id()
|
||||
agent_name = self._get_agent_name()
|
||||
if self.memory_type == "user":
|
||||
self.memory.add(value, user_id=user_id, metadata={**metadata})
|
||||
elif self.memory_type == "short_term":
|
||||
agent_name = self._get_agent_name()
|
||||
self.memory.add(
|
||||
value, agent_id=agent_name, metadata={"type": "short_term", **metadata}
|
||||
)
|
||||
elif self.memory_type == "long_term":
|
||||
agent_name = self._get_agent_name()
|
||||
self.memory.add(
|
||||
value,
|
||||
agent_id=agent_name,
|
||||
infer=False,
|
||||
metadata={"type": "long_term", **metadata},
|
||||
)
|
||||
elif self.memory_type == "entities":
|
||||
entity_name = None
|
||||
self.memory.add(
|
||||
value, user_id=entity_name, metadata={"type": "entity", **metadata}
|
||||
)
|
||||
|
||||
def search(
|
||||
self,
|
||||
query: str,
|
||||
limit: int = 3,
|
||||
score_threshold: float = 0.35,
|
||||
) -> List[Any]:
|
||||
params = {"query": query, "limit": limit}
|
||||
if self.memory_type == "user":
|
||||
user_id = self._get_user_id()
|
||||
params["user_id"] = user_id
|
||||
elif self.memory_type == "short_term":
|
||||
agent_name = self._get_agent_name()
|
||||
params["agent_id"] = agent_name
|
||||
params["metadata"] = {"type": "short_term"}
|
||||
elif self.memory_type == "long_term":
|
||||
agent_name = self._get_agent_name()
|
||||
params["agent_id"] = agent_name
|
||||
params["metadata"] = {"type": "long_term"}
|
||||
elif self.memory_type == "entities":
|
||||
agent_name = self._get_agent_name()
|
||||
params["agent_id"] = agent_name
|
||||
params["metadata"] = {"type": "entity"}
|
||||
|
||||
# Discard the filters for now since we create the filters
|
||||
# automatically when the crew is created.
|
||||
results = self.memory.search(**params)
|
||||
return [r for r in results if r["score"] >= score_threshold]
|
||||
|
||||
def _get_user_id(self):
|
||||
if self.memory_type == "user":
|
||||
if hasattr(self, "memory_config") and self.memory_config is not None:
|
||||
return self.memory_config.get("config", {}).get("user_id")
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
def _get_agent_name(self):
|
||||
agents = self.crew.agents if self.crew else []
|
||||
agents = [self._sanitize_role(agent.role) for agent in agents]
|
||||
agents = "_".join(agents)
|
||||
return agents
|
||||
@@ -4,13 +4,12 @@ import logging
|
||||
import os
|
||||
import shutil
|
||||
import uuid
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
from chromadb.api import ClientAPI
|
||||
from crewai.memory.storage.base_rag_storage import BaseRAGStorage
|
||||
from crewai.utilities.paths import db_storage_path
|
||||
from chromadb.api import ClientAPI
|
||||
from chromadb.api.types import validate_embedding_function
|
||||
from chromadb import Documents, EmbeddingFunction, Embeddings
|
||||
from typing import cast
|
||||
from crewai.utilities import EmbeddingConfigurator
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
@@ -21,9 +20,11 @@ def suppress_logging(
|
||||
logger = logging.getLogger(logger_name)
|
||||
original_level = logger.getEffectiveLevel()
|
||||
logger.setLevel(level)
|
||||
with contextlib.redirect_stdout(io.StringIO()), contextlib.redirect_stderr(
|
||||
io.StringIO()
|
||||
), contextlib.suppress(UserWarning):
|
||||
with (
|
||||
contextlib.redirect_stdout(io.StringIO()),
|
||||
contextlib.redirect_stderr(io.StringIO()),
|
||||
contextlib.suppress(UserWarning),
|
||||
):
|
||||
yield
|
||||
logger.setLevel(original_level)
|
||||
|
||||
@@ -49,77 +50,8 @@ class RAGStorage(BaseRAGStorage):
|
||||
self._initialize_app()
|
||||
|
||||
def _set_embedder_config(self):
|
||||
import chromadb.utils.embedding_functions as embedding_functions
|
||||
|
||||
if self.embedder_config is None:
|
||||
self.embedder_config = self._create_default_embedding_function()
|
||||
|
||||
if isinstance(self.embedder_config, dict):
|
||||
provider = self.embedder_config.get("provider")
|
||||
config = self.embedder_config.get("config", {})
|
||||
model_name = config.get("model")
|
||||
if provider == "openai":
|
||||
self.embedder_config = embedding_functions.OpenAIEmbeddingFunction(
|
||||
api_key=config.get("api_key") or os.getenv("OPENAI_API_KEY"),
|
||||
model_name=model_name,
|
||||
)
|
||||
elif provider == "azure":
|
||||
self.embedder_config = embedding_functions.OpenAIEmbeddingFunction(
|
||||
api_key=config.get("api_key"),
|
||||
api_base=config.get("api_base"),
|
||||
api_type=config.get("api_type", "azure"),
|
||||
api_version=config.get("api_version"),
|
||||
model_name=model_name,
|
||||
)
|
||||
elif provider == "ollama":
|
||||
from openai import OpenAI
|
||||
|
||||
class OllamaEmbeddingFunction(EmbeddingFunction):
|
||||
def __call__(self, input: Documents) -> Embeddings:
|
||||
client = OpenAI(
|
||||
base_url="http://localhost:11434/v1",
|
||||
api_key=config.get("api_key", "ollama"),
|
||||
)
|
||||
try:
|
||||
response = client.embeddings.create(
|
||||
input=input, model=model_name
|
||||
)
|
||||
embeddings = [item.embedding for item in response.data]
|
||||
return cast(Embeddings, embeddings)
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
self.embedder_config = OllamaEmbeddingFunction()
|
||||
elif provider == "vertexai":
|
||||
self.embedder_config = (
|
||||
embedding_functions.GoogleVertexEmbeddingFunction(
|
||||
model_name=model_name,
|
||||
api_key=config.get("api_key"),
|
||||
)
|
||||
)
|
||||
elif provider == "google":
|
||||
self.embedder_config = (
|
||||
embedding_functions.GoogleGenerativeAiEmbeddingFunction(
|
||||
model_name=model_name,
|
||||
api_key=config.get("api_key"),
|
||||
)
|
||||
)
|
||||
elif provider == "cohere":
|
||||
self.embedder_config = embedding_functions.CohereEmbeddingFunction(
|
||||
model_name=model_name,
|
||||
api_key=config.get("api_key"),
|
||||
)
|
||||
elif provider == "huggingface":
|
||||
self.embedder_config = embedding_functions.HuggingFaceEmbeddingServer(
|
||||
url=config.get("api_url"),
|
||||
)
|
||||
else:
|
||||
raise Exception(
|
||||
f"Unsupported embedding provider: {provider}, supported providers: [openai, azure, ollama, vertexai, google, cohere, huggingface]"
|
||||
)
|
||||
else:
|
||||
validate_embedding_function(self.embedder_config) # type: ignore # used for validating embedder_config if defined a embedding function/class
|
||||
self.embedder_config = self.embedder_config
|
||||
configurator = EmbeddingConfigurator()
|
||||
self.embedder_config = configurator.configure_embedder(self.embedder_config)
|
||||
|
||||
def _initialize_app(self):
|
||||
import chromadb
|
||||
@@ -211,8 +143,10 @@ class RAGStorage(BaseRAGStorage):
|
||||
)
|
||||
|
||||
def _create_default_embedding_function(self):
|
||||
import chromadb.utils.embedding_functions as embedding_functions
|
||||
from chromadb.utils.embedding_functions.openai_embedding_function import (
|
||||
OpenAIEmbeddingFunction,
|
||||
)
|
||||
|
||||
return embedding_functions.OpenAIEmbeddingFunction(
|
||||
return OpenAIEmbeddingFunction(
|
||||
api_key=os.getenv("OPENAI_API_KEY"), model_name="text-embedding-3-small"
|
||||
)
|
||||
|
||||
0
src/crewai/memory/user/__init__.py
Normal file
0
src/crewai/memory/user/__init__.py
Normal file
45
src/crewai/memory/user/user_memory.py
Normal file
45
src/crewai/memory/user/user_memory.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from crewai.memory.memory import Memory
|
||||
|
||||
|
||||
class UserMemory(Memory):
|
||||
"""
|
||||
UserMemory class for handling user memory storage and retrieval.
|
||||
Inherits from the Memory class and utilizes an instance of a class that
|
||||
adheres to the Storage for data storage, specifically working with
|
||||
MemoryItem instances.
|
||||
"""
|
||||
|
||||
def __init__(self, crew=None):
|
||||
try:
|
||||
from crewai.memory.storage.mem0_storage import Mem0Storage
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Mem0 is not installed. Please install it with `pip install mem0ai`."
|
||||
)
|
||||
storage = Mem0Storage(type="user", crew=crew)
|
||||
super().__init__(storage)
|
||||
|
||||
def save(
|
||||
self,
|
||||
value,
|
||||
metadata: Optional[Dict[str, Any]] = None,
|
||||
agent: Optional[str] = None,
|
||||
) -> None:
|
||||
# TODO: Change this function since we want to take care of the case where we save memories for the usr
|
||||
data = f"Remember the details about the user: {value}"
|
||||
super().save(data, metadata)
|
||||
|
||||
def search(
|
||||
self,
|
||||
query: str,
|
||||
limit: int = 3,
|
||||
score_threshold: float = 0.35,
|
||||
):
|
||||
results = super().search(
|
||||
query=query,
|
||||
limit=limit,
|
||||
score_threshold=score_threshold,
|
||||
)
|
||||
return results
|
||||
8
src/crewai/memory/user/user_memory_item.py
Normal file
8
src/crewai/memory/user/user_memory_item.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
|
||||
class UserMemoryItem:
|
||||
def __init__(self, data: Any, user: str, metadata: Optional[Dict[str, Any]] = None):
|
||||
self.data = data
|
||||
self.user = user
|
||||
self.metadata = metadata if metadata is not None else {}
|
||||
@@ -1,5 +1,7 @@
|
||||
from .annotations import (
|
||||
after_kickoff,
|
||||
agent,
|
||||
before_kickoff,
|
||||
cache_handler,
|
||||
callback,
|
||||
crew,
|
||||
@@ -26,4 +28,6 @@ __all__ = [
|
||||
"llm",
|
||||
"cache_handler",
|
||||
"pipeline",
|
||||
"before_kickoff",
|
||||
"after_kickoff",
|
||||
]
|
||||
|
||||
@@ -5,6 +5,16 @@ from crewai import Crew
|
||||
from crewai.project.utils import memoize
|
||||
|
||||
|
||||
def before_kickoff(func):
|
||||
func.is_before_kickoff = True
|
||||
return func
|
||||
|
||||
|
||||
def after_kickoff(func):
|
||||
func.is_after_kickoff = True
|
||||
return func
|
||||
|
||||
|
||||
def task(func):
|
||||
func.is_task = True
|
||||
|
||||
@@ -99,6 +109,19 @@ def crew(func) -> Callable[..., Crew]:
|
||||
self.agents = instantiated_agents
|
||||
self.tasks = instantiated_tasks
|
||||
|
||||
return func(self, *args, **kwargs)
|
||||
crew = func(self, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
def callback_wrapper(callback, instance):
|
||||
def wrapper(*args, **kwargs):
|
||||
return callback(instance, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
for _, callback in self._before_kickoff.items():
|
||||
crew.before_kickoff_callbacks.append(callback_wrapper(callback, self))
|
||||
for _, callback in self._after_kickoff.items():
|
||||
crew.after_kickoff_callbacks.append(callback_wrapper(callback, self))
|
||||
|
||||
return crew
|
||||
|
||||
return memoize(wrapper)
|
||||
|
||||
@@ -34,18 +34,39 @@ def CrewBase(cls: T) -> T:
|
||||
self.map_all_agent_variables()
|
||||
self.map_all_task_variables()
|
||||
|
||||
# Preserve task and agent information
|
||||
self._original_tasks = {
|
||||
# Preserve all decorated functions
|
||||
self._original_functions = {
|
||||
name: method
|
||||
for name, method in cls.__dict__.items()
|
||||
if hasattr(method, "is_task") and method.is_task
|
||||
}
|
||||
self._original_agents = {
|
||||
name: method
|
||||
for name, method in cls.__dict__.items()
|
||||
if hasattr(method, "is_agent") and method.is_agent
|
||||
if any(
|
||||
hasattr(method, attr)
|
||||
for attr in [
|
||||
"is_task",
|
||||
"is_agent",
|
||||
"is_before_kickoff",
|
||||
"is_after_kickoff",
|
||||
"is_kickoff",
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
# Store specific function types
|
||||
self._original_tasks = self._filter_functions(
|
||||
self._original_functions, "is_task"
|
||||
)
|
||||
self._original_agents = self._filter_functions(
|
||||
self._original_functions, "is_agent"
|
||||
)
|
||||
self._before_kickoff = self._filter_functions(
|
||||
self._original_functions, "is_before_kickoff"
|
||||
)
|
||||
self._after_kickoff = self._filter_functions(
|
||||
self._original_functions, "is_after_kickoff"
|
||||
)
|
||||
self._kickoff = self._filter_functions(
|
||||
self._original_functions, "is_kickoff"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def load_yaml(config_path: Path):
|
||||
try:
|
||||
|
||||
@@ -20,6 +20,7 @@ from pydantic import (
|
||||
from pydantic_core import PydanticCustomError
|
||||
|
||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||
from crewai.tools.base_tool import BaseTool
|
||||
from crewai.tasks.output_format import OutputFormat
|
||||
from crewai.tasks.task_output import TaskOutput
|
||||
from crewai.telemetry.telemetry import Telemetry
|
||||
@@ -91,7 +92,7 @@ class Task(BaseModel):
|
||||
output: Optional[TaskOutput] = Field(
|
||||
description="Task output, it's final result after being executed", default=None
|
||||
)
|
||||
tools: Optional[List[Any]] = Field(
|
||||
tools: Optional[List[BaseTool]] = Field(
|
||||
default_factory=list,
|
||||
description="Tools the agent is limited to use for this task.",
|
||||
)
|
||||
@@ -185,7 +186,7 @@ class Task(BaseModel):
|
||||
self,
|
||||
agent: Optional[BaseAgent] = None,
|
||||
context: Optional[str] = None,
|
||||
tools: Optional[List[Any]] = None,
|
||||
tools: Optional[List[BaseTool]] = None,
|
||||
) -> TaskOutput:
|
||||
"""Execute the task synchronously."""
|
||||
return self._execute_core(agent, context, tools)
|
||||
@@ -202,7 +203,7 @@ class Task(BaseModel):
|
||||
self,
|
||||
agent: BaseAgent | None = None,
|
||||
context: Optional[str] = None,
|
||||
tools: Optional[List[Any]] = None,
|
||||
tools: Optional[List[BaseTool]] = None,
|
||||
) -> Future[TaskOutput]:
|
||||
"""Execute the task asynchronously."""
|
||||
future: Future[TaskOutput] = Future()
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from .base_tool import BaseTool, tool
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
from crewai.agents.agent_builder.utilities.base_agent_tool import BaseAgentTools
|
||||
|
||||
|
||||
class AgentTools(BaseAgentTools):
|
||||
"""Default tools around agent delegation"""
|
||||
|
||||
def tools(self):
|
||||
from langchain.tools import StructuredTool
|
||||
|
||||
coworkers = ", ".join([f"{agent.role}" for agent in self.agents])
|
||||
tools = [
|
||||
StructuredTool.from_function(
|
||||
func=self.delegate_work,
|
||||
name="Delegate work to coworker",
|
||||
description=self.i18n.tools("delegate_work").format(
|
||||
coworkers=coworkers
|
||||
),
|
||||
),
|
||||
StructuredTool.from_function(
|
||||
func=self.ask_question,
|
||||
name="Ask question to coworker",
|
||||
description=self.i18n.tools("ask_question").format(coworkers=coworkers),
|
||||
),
|
||||
]
|
||||
return tools
|
||||
32
src/crewai/tools/agent_tools/agent_tools.py
Normal file
32
src/crewai/tools/agent_tools/agent_tools.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from crewai.tools.base_tool import BaseTool
|
||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||
from crewai.utilities import I18N
|
||||
|
||||
from .delegate_work_tool import DelegateWorkTool
|
||||
from .ask_question_tool import AskQuestionTool
|
||||
|
||||
|
||||
class AgentTools:
|
||||
"""Manager class for agent-related tools"""
|
||||
|
||||
def __init__(self, agents: list[BaseAgent], i18n: I18N = I18N()):
|
||||
self.agents = agents
|
||||
self.i18n = i18n
|
||||
|
||||
def tools(self) -> list[BaseTool]:
|
||||
"""Get all available agent tools"""
|
||||
coworkers = ", ".join([f"{agent.role}" for agent in self.agents])
|
||||
|
||||
delegate_tool = DelegateWorkTool(
|
||||
agents=self.agents,
|
||||
i18n=self.i18n,
|
||||
description=self.i18n.tools("delegate_work").format(coworkers=coworkers),
|
||||
)
|
||||
|
||||
ask_tool = AskQuestionTool(
|
||||
agents=self.agents,
|
||||
i18n=self.i18n,
|
||||
description=self.i18n.tools("ask_question").format(coworkers=coworkers),
|
||||
)
|
||||
|
||||
return [delegate_tool, ask_tool]
|
||||
26
src/crewai/tools/agent_tools/ask_question_tool.py
Normal file
26
src/crewai/tools/agent_tools/ask_question_tool.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from crewai.tools.agent_tools.base_agent_tools import BaseAgentTool
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class AskQuestionToolSchema(BaseModel):
|
||||
question: str = Field(..., description="The question to ask")
|
||||
context: str = Field(..., description="The context for the question")
|
||||
coworker: str = Field(..., description="The role/name of the coworker to ask")
|
||||
|
||||
|
||||
class AskQuestionTool(BaseAgentTool):
|
||||
"""Tool for asking questions to coworkers"""
|
||||
|
||||
name: str = "Ask question to coworker"
|
||||
args_schema: type[BaseModel] = AskQuestionToolSchema
|
||||
|
||||
def _run(
|
||||
self,
|
||||
question: str,
|
||||
context: str,
|
||||
coworker: Optional[str] = None,
|
||||
**kwargs,
|
||||
) -> str:
|
||||
coworker = self._get_coworker(coworker, **kwargs)
|
||||
return self._execute(coworker, question, context)
|
||||
@@ -1,22 +1,19 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, Union
|
||||
from pydantic import Field
|
||||
|
||||
from crewai.tools.base_tool import BaseTool
|
||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||
from crewai.task import Task
|
||||
from crewai.utilities import I18N
|
||||
|
||||
|
||||
class BaseAgentTools(BaseModel, ABC):
|
||||
"""Default tools around agent delegation"""
|
||||
class BaseAgentTool(BaseTool):
|
||||
"""Base class for agent-related tools"""
|
||||
|
||||
agents: List[BaseAgent] = Field(description="List of agents in this crew.")
|
||||
i18n: I18N = Field(default=I18N(), description="Internationalization settings.")
|
||||
|
||||
@abstractmethod
|
||||
def tools(self):
|
||||
pass
|
||||
agents: list[BaseAgent] = Field(description="List of available agents")
|
||||
i18n: I18N = Field(
|
||||
default_factory=I18N, description="Internationalization settings"
|
||||
)
|
||||
|
||||
def _get_coworker(self, coworker: Optional[str], **kwargs) -> Optional[str]:
|
||||
coworker = coworker or kwargs.get("co_worker") or kwargs.get("coworker")
|
||||
@@ -24,27 +21,11 @@ class BaseAgentTools(BaseModel, ABC):
|
||||
is_list = coworker.startswith("[") and coworker.endswith("]")
|
||||
if is_list:
|
||||
coworker = coworker[1:-1].split(",")[0]
|
||||
|
||||
return coworker
|
||||
|
||||
def delegate_work(
|
||||
self, task: str, context: str, coworker: Optional[str] = None, **kwargs
|
||||
):
|
||||
"""Useful to delegate a specific task to a coworker passing all necessary context and names."""
|
||||
coworker = self._get_coworker(coworker, **kwargs)
|
||||
return self._execute(coworker, task, context)
|
||||
|
||||
def ask_question(
|
||||
self, question: str, context: str, coworker: Optional[str] = None, **kwargs
|
||||
):
|
||||
"""Useful to ask a question, opinion or take from a coworker passing all necessary context and names."""
|
||||
coworker = self._get_coworker(coworker, **kwargs)
|
||||
return self._execute(coworker, question, context)
|
||||
|
||||
def _execute(
|
||||
self, agent_name: Union[str, None], task: str, context: Union[str, None]
|
||||
):
|
||||
"""Execute the command."""
|
||||
) -> str:
|
||||
try:
|
||||
if agent_name is None:
|
||||
agent_name = ""
|
||||
@@ -57,7 +38,6 @@ class BaseAgentTools(BaseModel, ABC):
|
||||
# when it should look like this:
|
||||
# {"task": "....", "coworker": "...."}
|
||||
agent_name = agent_name.casefold().replace('"', "").replace("\n", "")
|
||||
|
||||
agent = [ # type: ignore # Incompatible types in assignment (expression has type "list[BaseAgent]", variable has type "str | None")
|
||||
available_agent
|
||||
for available_agent in self.agents
|
||||
29
src/crewai/tools/agent_tools/delegate_work_tool.py
Normal file
29
src/crewai/tools/agent_tools/delegate_work_tool.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from crewai.tools.agent_tools.base_agent_tools import BaseAgentTool
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class DelegateWorkToolSchema(BaseModel):
|
||||
task: str = Field(..., description="The task to delegate")
|
||||
context: str = Field(..., description="The context for the task")
|
||||
coworker: str = Field(
|
||||
..., description="The role/name of the coworker to delegate to"
|
||||
)
|
||||
|
||||
|
||||
class DelegateWorkTool(BaseAgentTool):
|
||||
"""Tool for delegating work to coworkers"""
|
||||
|
||||
name: str = "Delegate work to coworker"
|
||||
args_schema: type[BaseModel] = DelegateWorkToolSchema
|
||||
|
||||
def _run(
|
||||
self,
|
||||
task: str,
|
||||
context: str,
|
||||
coworker: Optional[str] = None,
|
||||
**kwargs,
|
||||
) -> str:
|
||||
coworker = self._get_coworker(coworker, **kwargs)
|
||||
return self._execute(coworker, task, context)
|
||||
186
src/crewai/tools/base_tool.py
Normal file
186
src/crewai/tools/base_tool.py
Normal file
@@ -0,0 +1,186 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Callable, Type, get_args, get_origin
|
||||
|
||||
from langchain_core.tools import StructuredTool
|
||||
from pydantic import BaseModel, ConfigDict, Field, validator
|
||||
from pydantic import BaseModel as PydanticBaseModel
|
||||
|
||||
|
||||
class BaseTool(BaseModel, ABC):
|
||||
class _ArgsSchemaPlaceholder(PydanticBaseModel):
|
||||
pass
|
||||
|
||||
model_config = ConfigDict()
|
||||
|
||||
name: str
|
||||
"""The unique name of the tool that clearly communicates its purpose."""
|
||||
description: str
|
||||
"""Used to tell the model how/when/why to use the tool."""
|
||||
args_schema: Type[PydanticBaseModel] = Field(default_factory=_ArgsSchemaPlaceholder)
|
||||
"""The schema for the arguments that the tool accepts."""
|
||||
description_updated: bool = False
|
||||
"""Flag to check if the description has been updated."""
|
||||
cache_function: Callable = lambda _args=None, _result=None: True
|
||||
"""Function that will be used to determine if the tool should be cached, should return a boolean. If None, the tool will be cached."""
|
||||
result_as_answer: bool = False
|
||||
"""Flag to check if the tool should be the final agent answer."""
|
||||
|
||||
@validator("args_schema", always=True, pre=True)
|
||||
def _default_args_schema(
|
||||
cls, v: Type[PydanticBaseModel]
|
||||
) -> Type[PydanticBaseModel]:
|
||||
if not isinstance(v, cls._ArgsSchemaPlaceholder):
|
||||
return v
|
||||
|
||||
return type(
|
||||
f"{cls.__name__}Schema",
|
||||
(PydanticBaseModel,),
|
||||
{
|
||||
"__annotations__": {
|
||||
k: v for k, v in cls._run.__annotations__.items() if k != "return"
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
def model_post_init(self, __context: Any) -> None:
|
||||
self._generate_description()
|
||||
|
||||
super().model_post_init(__context)
|
||||
|
||||
def run(
|
||||
self,
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> Any:
|
||||
print(f"Using Tool: {self.name}")
|
||||
return self._run(*args, **kwargs)
|
||||
|
||||
@abstractmethod
|
||||
def _run(
|
||||
self,
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> Any:
|
||||
"""Here goes the actual implementation of the tool."""
|
||||
|
||||
def to_langchain(self) -> StructuredTool:
|
||||
self._set_args_schema()
|
||||
return StructuredTool(
|
||||
name=self.name,
|
||||
description=self.description,
|
||||
args_schema=self.args_schema,
|
||||
func=self._run,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_langchain(cls, tool: StructuredTool) -> "BaseTool":
|
||||
if cls == Tool:
|
||||
if tool.func is None:
|
||||
raise ValueError("StructuredTool must have a callable 'func'")
|
||||
return Tool(
|
||||
name=tool.name,
|
||||
description=tool.description,
|
||||
args_schema=tool.args_schema,
|
||||
func=tool.func,
|
||||
)
|
||||
raise NotImplementedError(f"from_langchain not implemented for {cls.__name__}")
|
||||
|
||||
def _set_args_schema(self):
|
||||
if self.args_schema is None:
|
||||
class_name = f"{self.__class__.__name__}Schema"
|
||||
self.args_schema = type(
|
||||
class_name,
|
||||
(PydanticBaseModel,),
|
||||
{
|
||||
"__annotations__": {
|
||||
k: v
|
||||
for k, v in self._run.__annotations__.items()
|
||||
if k != "return"
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
def _generate_description(self):
|
||||
args_schema = {
|
||||
name: {
|
||||
"description": field.description,
|
||||
"type": BaseTool._get_arg_annotations(field.annotation),
|
||||
}
|
||||
for name, field in self.args_schema.model_fields.items()
|
||||
}
|
||||
|
||||
self.description = f"Tool Name: {self.name}\nTool Arguments: {args_schema}\nTool Description: {self.description}"
|
||||
|
||||
@staticmethod
|
||||
def _get_arg_annotations(annotation: type[Any] | None) -> str:
|
||||
if annotation is None:
|
||||
return "None"
|
||||
|
||||
origin = get_origin(annotation)
|
||||
args = get_args(annotation)
|
||||
|
||||
if origin is None:
|
||||
return (
|
||||
annotation.__name__
|
||||
if hasattr(annotation, "__name__")
|
||||
else str(annotation)
|
||||
)
|
||||
|
||||
if args:
|
||||
args_str = ", ".join(BaseTool._get_arg_annotations(arg) for arg in args)
|
||||
return f"{origin.__name__}[{args_str}]"
|
||||
|
||||
return origin.__name__
|
||||
|
||||
|
||||
class Tool(BaseTool):
|
||||
func: Callable
|
||||
"""The function that will be executed when the tool is called."""
|
||||
|
||||
def _run(self, *args: Any, **kwargs: Any) -> Any:
|
||||
return self.func(*args, **kwargs)
|
||||
|
||||
|
||||
def to_langchain(
|
||||
tools: list[BaseTool | StructuredTool],
|
||||
) -> list[StructuredTool]:
|
||||
return [t.to_langchain() if isinstance(t, BaseTool) else t for t in tools]
|
||||
|
||||
|
||||
def tool(*args):
|
||||
"""
|
||||
Decorator to create a tool from a function.
|
||||
"""
|
||||
|
||||
def _make_with_name(tool_name: str) -> Callable:
|
||||
def _make_tool(f: Callable) -> BaseTool:
|
||||
if f.__doc__ is None:
|
||||
raise ValueError("Function must have a docstring")
|
||||
if f.__annotations__ is None:
|
||||
raise ValueError("Function must have type annotations")
|
||||
|
||||
class_name = "".join(tool_name.split()).title()
|
||||
args_schema = type(
|
||||
class_name,
|
||||
(PydanticBaseModel,),
|
||||
{
|
||||
"__annotations__": {
|
||||
k: v for k, v in f.__annotations__.items() if k != "return"
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
return Tool(
|
||||
name=tool_name,
|
||||
description=f.__doc__,
|
||||
func=f,
|
||||
args_schema=args_schema,
|
||||
)
|
||||
|
||||
return _make_tool
|
||||
|
||||
if len(args) == 1 and callable(args[0]):
|
||||
return _make_with_name(args[0].__name__)(args[0])
|
||||
if len(args) == 1 and isinstance(args[0], str):
|
||||
return _make_with_name(args[0])
|
||||
raise ValueError("Invalid arguments")
|
||||
0
src/crewai/tools/cache_tools/__init__.py
Normal file
0
src/crewai/tools/cache_tools/__init__.py
Normal file
@@ -10,6 +10,7 @@ import crewai.utilities.events as events
|
||||
from crewai.agents.tools_handler import ToolsHandler
|
||||
from crewai.task import Task
|
||||
from crewai.telemetry import Telemetry
|
||||
from crewai.tools import BaseTool
|
||||
from crewai.tools.tool_calling import InstructorToolCalling, ToolCalling
|
||||
from crewai.tools.tool_usage_events import ToolUsageError, ToolUsageFinished
|
||||
from crewai.utilities import I18N, Converter, ConverterError, Printer
|
||||
@@ -49,7 +50,7 @@ class ToolUsage:
|
||||
def __init__(
|
||||
self,
|
||||
tools_handler: ToolsHandler,
|
||||
tools: List[Any],
|
||||
tools: List[BaseTool],
|
||||
original_tools: List[Any],
|
||||
tools_description: str,
|
||||
tools_names: str,
|
||||
@@ -298,22 +299,7 @@ class ToolUsage:
|
||||
"""Render the tool name and description in plain text."""
|
||||
descriptions = []
|
||||
for tool in self.tools:
|
||||
args = {
|
||||
name: {
|
||||
"description": field.description,
|
||||
"type": field.annotation.__name__,
|
||||
}
|
||||
for name, field in tool.args_schema.model_fields.items()
|
||||
}
|
||||
descriptions.append(
|
||||
"\n".join(
|
||||
[
|
||||
f"Tool Name: {tool.name.lower()}",
|
||||
f"Tool Description: {tool.description}",
|
||||
f"Tool Arguments: {args}",
|
||||
]
|
||||
)
|
||||
)
|
||||
descriptions.append(tool.description)
|
||||
return "\n--\n".join(descriptions)
|
||||
|
||||
def _function_calling(self, tool_string: str):
|
||||
|
||||
@@ -8,6 +8,7 @@ class UsageMetrics(BaseModel):
|
||||
Attributes:
|
||||
total_tokens: Total number of tokens used.
|
||||
prompt_tokens: Number of tokens used in prompts.
|
||||
cached_prompt_tokens: Number of cached prompt tokens used.
|
||||
completion_tokens: Number of tokens used in completions.
|
||||
successful_requests: Number of successful requests made.
|
||||
"""
|
||||
@@ -16,6 +17,9 @@ class UsageMetrics(BaseModel):
|
||||
prompt_tokens: int = Field(
|
||||
default=0, description="Number of tokens used in prompts."
|
||||
)
|
||||
cached_prompt_tokens: int = Field(
|
||||
default=0, description="Number of cached prompt tokens used."
|
||||
)
|
||||
completion_tokens: int = Field(
|
||||
default=0, description="Number of tokens used in completions."
|
||||
)
|
||||
@@ -32,5 +36,6 @@ class UsageMetrics(BaseModel):
|
||||
"""
|
||||
self.total_tokens += usage_metrics.total_tokens
|
||||
self.prompt_tokens += usage_metrics.prompt_tokens
|
||||
self.cached_prompt_tokens += usage_metrics.cached_prompt_tokens
|
||||
self.completion_tokens += usage_metrics.completion_tokens
|
||||
self.successful_requests += usage_metrics.successful_requests
|
||||
|
||||
@@ -10,6 +10,7 @@ from .rpm_controller import RPMController
|
||||
from .exceptions.context_window_exceeding_exception import (
|
||||
LLMContextLengthExceededException,
|
||||
)
|
||||
from .embedding_configurator import EmbeddingConfigurator
|
||||
|
||||
__all__ = [
|
||||
"Converter",
|
||||
@@ -23,4 +24,5 @@ __all__ = [
|
||||
"RPMController",
|
||||
"YamlParser",
|
||||
"LLMContextLengthExceededException",
|
||||
"EmbeddingConfigurator",
|
||||
]
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
TRAINING_DATA_FILE = "training_data.pkl"
|
||||
TRAINED_AGENTS_DATA_FILE = "trained_agents_data.pkl"
|
||||
DEFAULT_SCORE_THRESHOLD = 0.35
|
||||
|
||||
183
src/crewai/utilities/embedding_configurator.py
Normal file
183
src/crewai/utilities/embedding_configurator.py
Normal file
@@ -0,0 +1,183 @@
|
||||
import os
|
||||
from typing import Any, Dict, cast
|
||||
from chromadb import EmbeddingFunction, Documents, Embeddings
|
||||
from chromadb.api.types import validate_embedding_function
|
||||
|
||||
|
||||
class EmbeddingConfigurator:
|
||||
def __init__(self):
|
||||
self.embedding_functions = {
|
||||
"openai": self._configure_openai,
|
||||
"azure": self._configure_azure,
|
||||
"ollama": self._configure_ollama,
|
||||
"vertexai": self._configure_vertexai,
|
||||
"google": self._configure_google,
|
||||
"cohere": self._configure_cohere,
|
||||
"bedrock": self._configure_bedrock,
|
||||
"huggingface": self._configure_huggingface,
|
||||
"watson": self._configure_watson,
|
||||
}
|
||||
|
||||
def configure_embedder(
|
||||
self,
|
||||
embedder_config: Dict[str, Any] | None = None,
|
||||
) -> EmbeddingFunction:
|
||||
"""Configures and returns an embedding function based on the provided config."""
|
||||
if embedder_config is None:
|
||||
return self._create_default_embedding_function()
|
||||
|
||||
provider = embedder_config.get("provider")
|
||||
config = embedder_config.get("config", {})
|
||||
model_name = config.get("model")
|
||||
|
||||
if isinstance(provider, EmbeddingFunction):
|
||||
try:
|
||||
validate_embedding_function(provider)
|
||||
return provider
|
||||
except Exception as e:
|
||||
raise ValueError(f"Invalid custom embedding function: {str(e)}")
|
||||
|
||||
if provider not in self.embedding_functions:
|
||||
raise Exception(
|
||||
f"Unsupported embedding provider: {provider}, supported providers: {list(self.embedding_functions.keys())}"
|
||||
)
|
||||
|
||||
return self.embedding_functions[provider](config, model_name)
|
||||
|
||||
@staticmethod
|
||||
def _create_default_embedding_function():
|
||||
from chromadb.utils.embedding_functions.openai_embedding_function import (
|
||||
OpenAIEmbeddingFunction,
|
||||
)
|
||||
|
||||
return OpenAIEmbeddingFunction(
|
||||
api_key=os.getenv("OPENAI_API_KEY"), model_name="text-embedding-3-small"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _configure_openai(config, model_name):
|
||||
from chromadb.utils.embedding_functions.openai_embedding_function import (
|
||||
OpenAIEmbeddingFunction,
|
||||
)
|
||||
|
||||
return OpenAIEmbeddingFunction(
|
||||
api_key=config.get("api_key") or os.getenv("OPENAI_API_KEY"),
|
||||
model_name=model_name,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _configure_azure(config, model_name):
|
||||
from chromadb.utils.embedding_functions.openai_embedding_function import (
|
||||
OpenAIEmbeddingFunction,
|
||||
)
|
||||
|
||||
return OpenAIEmbeddingFunction(
|
||||
api_key=config.get("api_key"),
|
||||
api_base=config.get("api_base"),
|
||||
api_type=config.get("api_type", "azure"),
|
||||
api_version=config.get("api_version"),
|
||||
model_name=model_name,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _configure_ollama(config, model_name):
|
||||
from chromadb.utils.embedding_functions.ollama_embedding_function import (
|
||||
OllamaEmbeddingFunction,
|
||||
)
|
||||
|
||||
return OllamaEmbeddingFunction(
|
||||
url=config.get("url", "http://localhost:11434/api/embeddings"),
|
||||
model_name=model_name,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _configure_vertexai(config, model_name):
|
||||
from chromadb.utils.embedding_functions.google_embedding_function import (
|
||||
GoogleVertexEmbeddingFunction,
|
||||
)
|
||||
|
||||
return GoogleVertexEmbeddingFunction(
|
||||
model_name=model_name,
|
||||
api_key=config.get("api_key"),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _configure_google(config, model_name):
|
||||
from chromadb.utils.embedding_functions.google_embedding_function import (
|
||||
GoogleGenerativeAiEmbeddingFunction,
|
||||
)
|
||||
|
||||
return GoogleGenerativeAiEmbeddingFunction(
|
||||
model_name=model_name,
|
||||
api_key=config.get("api_key"),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _configure_cohere(config, model_name):
|
||||
from chromadb.utils.embedding_functions.cohere_embedding_function import (
|
||||
CohereEmbeddingFunction,
|
||||
)
|
||||
|
||||
return CohereEmbeddingFunction(
|
||||
model_name=model_name,
|
||||
api_key=config.get("api_key"),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _configure_bedrock(config, model_name):
|
||||
from chromadb.utils.embedding_functions.amazon_bedrock_embedding_function import (
|
||||
AmazonBedrockEmbeddingFunction,
|
||||
)
|
||||
|
||||
return AmazonBedrockEmbeddingFunction(
|
||||
session=config.get("session"),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _configure_huggingface(config, model_name):
|
||||
from chromadb.utils.embedding_functions.huggingface_embedding_function import (
|
||||
HuggingFaceEmbeddingServer,
|
||||
)
|
||||
|
||||
return HuggingFaceEmbeddingServer(
|
||||
url=config.get("api_url"),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _configure_watson(config, model_name):
|
||||
try:
|
||||
import ibm_watsonx_ai.foundation_models as watson_models
|
||||
from ibm_watsonx_ai import Credentials
|
||||
from ibm_watsonx_ai.metanames import EmbedTextParamsMetaNames as EmbedParams
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"IBM Watson dependencies are not installed. Please install them to use Watson embedding."
|
||||
) from e
|
||||
|
||||
class WatsonEmbeddingFunction(EmbeddingFunction):
|
||||
def __call__(self, input: Documents) -> Embeddings:
|
||||
if isinstance(input, str):
|
||||
input = [input]
|
||||
|
||||
embed_params = {
|
||||
EmbedParams.TRUNCATE_INPUT_TOKENS: 3,
|
||||
EmbedParams.RETURN_OPTIONS: {"input_text": True},
|
||||
}
|
||||
|
||||
embedding = watson_models.Embeddings(
|
||||
model_id=config.get("model"),
|
||||
params=embed_params,
|
||||
credentials=Credentials(
|
||||
api_key=config.get("api_key"), url=config.get("api_url")
|
||||
),
|
||||
project_id=config.get("project_id"),
|
||||
)
|
||||
|
||||
try:
|
||||
embeddings = embedding.embed_documents(input)
|
||||
return cast(Embeddings, embeddings)
|
||||
except Exception as e:
|
||||
print("Error during Watson embedding:", e)
|
||||
raise e
|
||||
|
||||
return WatsonEmbeddingFunction()
|
||||
@@ -16,7 +16,11 @@ class FileHandler:
|
||||
|
||||
def log(self, **kwargs):
|
||||
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
message = f"{now}: " + ", ".join([f"{key}=\"{value}\"" for key, value in kwargs.items()]) + "\n"
|
||||
message = (
|
||||
f"{now}: "
|
||||
+ ", ".join([f'{key}="{value}"' for key, value in kwargs.items()])
|
||||
+ "\n"
|
||||
)
|
||||
with open(self._path, "a", encoding="utf-8") as file:
|
||||
file.write(message + "\n")
|
||||
|
||||
@@ -63,7 +67,7 @@ class PickleHandler:
|
||||
|
||||
with open(self.file_path, "rb") as file:
|
||||
try:
|
||||
return pickle.load(file)
|
||||
return pickle.load(file) # nosec
|
||||
except EOFError:
|
||||
return {} # Return an empty dictionary if the file is empty or corrupted
|
||||
except Exception:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from litellm.integrations.custom_logger import CustomLogger
|
||||
|
||||
from litellm.types.utils import Usage
|
||||
from crewai.agents.agent_builder.utilities.base_token_process import TokenProcess
|
||||
|
||||
|
||||
@@ -11,8 +11,11 @@ class TokenCalcHandler(CustomLogger):
|
||||
if self.token_cost_process is None:
|
||||
return
|
||||
|
||||
usage : Usage = response_obj["usage"]
|
||||
self.token_cost_process.sum_successful_requests(1)
|
||||
self.token_cost_process.sum_prompt_tokens(response_obj["usage"].prompt_tokens)
|
||||
self.token_cost_process.sum_completion_tokens(
|
||||
response_obj["usage"].completion_tokens
|
||||
)
|
||||
self.token_cost_process.sum_prompt_tokens(usage.prompt_tokens)
|
||||
self.token_cost_process.sum_completion_tokens(usage.completion_tokens)
|
||||
if usage.prompt_tokens_details:
|
||||
self.token_cost_process.sum_cached_prompt_tokens(
|
||||
usage.prompt_tokens_details.cached_tokens
|
||||
)
|
||||
|
||||
@@ -5,13 +5,14 @@ from unittest import mock
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from crewai_tools import tool
|
||||
|
||||
from crewai import Agent, Crew, Task
|
||||
from crewai.agents.cache import CacheHandler
|
||||
from crewai.agents.crew_agent_executor import CrewAgentExecutor
|
||||
from crewai.agents.parser import AgentAction, CrewAgentParser, OutputParserException
|
||||
from crewai.knowledge.source.string_knowledge_source import StringKnowledgeSource
|
||||
from crewai.llm import LLM
|
||||
from crewai.tools import tool
|
||||
from crewai.tools.tool_calling import InstructorToolCalling
|
||||
from crewai.tools.tool_usage import ToolUsage
|
||||
from crewai.tools.tool_usage_events import ToolUsageFinished
|
||||
@@ -277,9 +278,10 @@ def test_cache_hitting():
|
||||
"multiplier-{'first_number': 12, 'second_number': 3}": 36,
|
||||
}
|
||||
|
||||
with patch.object(CacheHandler, "read") as read, patch.object(
|
||||
Emitter, "emit"
|
||||
) as emit:
|
||||
with (
|
||||
patch.object(CacheHandler, "read") as read,
|
||||
patch.object(Emitter, "emit") as emit,
|
||||
):
|
||||
read.return_value = "0"
|
||||
task = Task(
|
||||
description="What is 2 times 6? Ignore correctness and just return the result of the multiplication tool, you must use the tool.",
|
||||
@@ -604,7 +606,7 @@ def test_agent_respect_the_max_rpm_set(capsys):
|
||||
def test_agent_respect_the_max_rpm_set_over_crew_rpm(capsys):
|
||||
from unittest.mock import patch
|
||||
|
||||
from crewai_tools import tool
|
||||
from crewai.tools import tool
|
||||
|
||||
@tool
|
||||
def get_final_answer() -> float:
|
||||
@@ -642,7 +644,7 @@ def test_agent_respect_the_max_rpm_set_over_crew_rpm(capsys):
|
||||
def test_agent_without_max_rpm_respet_crew_rpm(capsys):
|
||||
from unittest.mock import patch
|
||||
|
||||
from crewai_tools import tool
|
||||
from crewai.tools import tool
|
||||
|
||||
@tool
|
||||
def get_final_answer() -> float:
|
||||
@@ -696,7 +698,7 @@ def test_agent_without_max_rpm_respet_crew_rpm(capsys):
|
||||
def test_agent_error_on_parsing_tool(capsys):
|
||||
from unittest.mock import patch
|
||||
|
||||
from crewai_tools import tool
|
||||
from crewai.tools import tool
|
||||
|
||||
@tool
|
||||
def get_final_answer() -> float:
|
||||
@@ -739,7 +741,7 @@ def test_agent_error_on_parsing_tool(capsys):
|
||||
def test_agent_remembers_output_format_after_using_tools_too_many_times():
|
||||
from unittest.mock import patch
|
||||
|
||||
from crewai_tools import tool
|
||||
from crewai.tools import tool
|
||||
|
||||
@tool
|
||||
def get_final_answer() -> float:
|
||||
@@ -863,11 +865,16 @@ def test_agent_function_calling_llm():
|
||||
|
||||
from crewai.tools.tool_usage import ToolUsage
|
||||
|
||||
with patch.object(
|
||||
instructor, "from_litellm", wraps=instructor.from_litellm
|
||||
) as mock_from_litellm, patch.object(
|
||||
ToolUsage, "_original_tool_calling", side_effect=Exception("Forced exception")
|
||||
) as mock_original_tool_calling:
|
||||
with (
|
||||
patch.object(
|
||||
instructor, "from_litellm", wraps=instructor.from_litellm
|
||||
) as mock_from_litellm,
|
||||
patch.object(
|
||||
ToolUsage,
|
||||
"_original_tool_calling",
|
||||
side_effect=Exception("Forced exception"),
|
||||
) as mock_original_tool_calling,
|
||||
):
|
||||
crew.kickoff()
|
||||
mock_from_litellm.assert_called()
|
||||
mock_original_tool_calling.assert_called()
|
||||
@@ -894,7 +901,7 @@ def test_agent_count_formatting_error():
|
||||
|
||||
@pytest.mark.vcr(filter_headers=["authorization"])
|
||||
def test_tool_result_as_answer_is_the_final_answer_for_the_agent():
|
||||
from crewai_tools import BaseTool
|
||||
from crewai.tools import BaseTool
|
||||
|
||||
class MyCustomTool(BaseTool):
|
||||
name: str = "Get Greetings"
|
||||
@@ -924,7 +931,7 @@ def test_tool_result_as_answer_is_the_final_answer_for_the_agent():
|
||||
|
||||
@pytest.mark.vcr(filter_headers=["authorization"])
|
||||
def test_tool_usage_information_is_appended_to_agent():
|
||||
from crewai_tools import BaseTool
|
||||
from crewai.tools import BaseTool
|
||||
|
||||
class MyCustomTool(BaseTool):
|
||||
name: str = "Decide Greetings"
|
||||
@@ -1568,3 +1575,42 @@ def test_agent_execute_task_with_ollama():
|
||||
result = agent.execute_task(task)
|
||||
assert len(result.split(".")) == 2
|
||||
assert "AI" in result or "artificial intelligence" in result.lower()
|
||||
|
||||
|
||||
@pytest.mark.vcr(filter_headers=["authorization"])
|
||||
def test_agent_with_knowledge_sources():
|
||||
# Create a knowledge source with some content
|
||||
content = "Brandon's favorite color is blue and he likes Mexican food."
|
||||
string_source = StringKnowledgeSource(
|
||||
content=content, metadata={"preference": "personal"}
|
||||
)
|
||||
|
||||
|
||||
with patch('crewai.knowledge.storage.knowledge_storage.KnowledgeStorage') as MockKnowledge:
|
||||
mock_knowledge_instance = MockKnowledge.return_value
|
||||
mock_knowledge_instance.sources = [string_source]
|
||||
mock_knowledge_instance.query.return_value = [{
|
||||
"content": content,
|
||||
"metadata": {"preference": "personal"}
|
||||
}]
|
||||
|
||||
agent = Agent(
|
||||
role="Information Agent",
|
||||
goal="Provide information based on knowledge sources",
|
||||
backstory="You have access to specific knowledge sources.",
|
||||
llm=LLM(model="gpt-4o-mini"),
|
||||
)
|
||||
|
||||
# Create a task that requires the agent to use the knowledge
|
||||
task = Task(
|
||||
description="What is Brandon's favorite color?",
|
||||
expected_output="Brandon's favorite color.",
|
||||
agent=agent,
|
||||
)
|
||||
|
||||
crew = Crew(agents=[agent], tasks=[task])
|
||||
result = crew.kickoff()
|
||||
|
||||
# Assert that the agent provides the correct information
|
||||
assert "blue" in result.raw.lower()
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import hashlib
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||
from crewai.tools.base_tool import BaseTool
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
@@ -10,13 +11,13 @@ class TestAgent(BaseAgent):
|
||||
self,
|
||||
task: Any,
|
||||
context: Optional[str] = None,
|
||||
tools: Optional[List[Any]] = None,
|
||||
tools: Optional[List[BaseTool]] = None,
|
||||
) -> str:
|
||||
return ""
|
||||
|
||||
def create_agent_executor(self, tools=None) -> None: ...
|
||||
|
||||
def _parse_tools(self, tools: List[Any]) -> List[Any]:
|
||||
def _parse_tools(self, tools: List[BaseTool]) -> List[BaseTool]:
|
||||
return []
|
||||
|
||||
def get_delegation_tools(self, agents: List["BaseAgent"]): ...
|
||||
|
||||
449
tests/cassettes/test_after_crew_modification.yaml
Normal file
449
tests/cassettes/test_after_crew_modification.yaml
Normal file
@@ -0,0 +1,449 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: !!binary |
|
||||
CuMOCiQKIgoMc2VydmljZS5uYW1lEhIKEGNyZXdBSS10ZWxlbWV0cnkSug4KEgoQY3Jld2FpLnRl
|
||||
bGVtZXRyeRKSDAoQK+dPhrB8w3HKFlxX60XzYRIIk5aB+A8oCWQqDENyZXcgQ3JlYXRlZDABObix
|
||||
K+HWrwgYQcBiMeHWrwgYShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuODAuMEoaCg5weXRob25fdmVy
|
||||
c2lvbhIICgYzLjExLjdKLgoIY3Jld19rZXkSIgogZjM0NmE5YWQ2ZDczMDYzZTA2NzdiMTdjZTlj
|
||||
NTAxNzdKMQoHY3Jld19pZBImCiQ3NjRjZWM1YS04NzkxLTRmN2MtOWY0MC1hNTMzMzJmOTk3YzBK
|
||||
HAoMY3Jld19wcm9jZXNzEgwKCnNlcXVlbnRpYWxKEQoLY3Jld19tZW1vcnkSAhAAShoKFGNyZXdf
|
||||
bnVtYmVyX29mX3Rhc2tzEgIYAkobChVjcmV3X251bWJlcl9vZl9hZ2VudHMSAhgCSqwFCgtjcmV3
|
||||
X2FnZW50cxKcBQqZBVt7ImtleSI6ICI3M2MzNDljOTNjMTYzYjVkNGRmOThhNjRmYWMxYzQzMCIs
|
||||
ICJpZCI6ICJjZDgwYjlhNy1hN2QzLTQzNTQtYjUyOC1jMzAyODA0MjA3YzgiLCAicm9sZSI6ICJ7
|
||||
dG9waWN9IFNlbmlvciBEYXRhIFJlc2VhcmNoZXJcbiIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4
|
||||
X2l0ZXIiOiAyMCwgIm1heF9ycG0iOiBudWxsLCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwg
|
||||
ImxsbSI6ICJncHQtNG8iLCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNlLCAiYWxsb3dfY29k
|
||||
ZV9leGVjdXRpb24/IjogZmFsc2UsICJtYXhfcmV0cnlfbGltaXQiOiAyLCAidG9vbHNfbmFtZXMi
|
||||
OiBbXX0sIHsia2V5IjogImJiMDY4Mzc3YzE2NDFiZTZkN2Q5N2E1MTY1OWRiNjEzIiwgImlkIjog
|
||||
ImJmZjc3YmUyLWU4MjQtNGEyOS1hZTFlLTQyMWFjMzc2MjY2YyIsICJyb2xlIjogInt0b3BpY30g
|
||||
UmVwb3J0aW5nIEFuYWx5c3RcbiIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4X2l0ZXIiOiAyMCwg
|
||||
Im1heF9ycG0iOiBudWxsLCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwgImxsbSI6ICJncHQt
|
||||
NG8iLCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNlLCAiYWxsb3dfY29kZV9leGVjdXRpb24/
|
||||
IjogZmFsc2UsICJtYXhfcmV0cnlfbGltaXQiOiAyLCAidG9vbHNfbmFtZXMiOiBbXX1dSpMECgpj
|
||||
cmV3X3Rhc2tzEoQECoEEW3sia2V5IjogIjZhZmM0YjM5NjI1OWZiYjc2ODFmNTZjNzc1NWNjOTM3
|
||||
IiwgImlkIjogIjRmNTFlYzM2LTVlMDctNGU4Ni1iYzIxLWU1MTQ0Mzg2YmIyYSIsICJhc3luY19l
|
||||
eGVjdXRpb24/IjogZmFsc2UsICJodW1hbl9pbnB1dD8iOiBmYWxzZSwgImFnZW50X3JvbGUiOiAi
|
||||
e3RvcGljfSBTZW5pb3IgRGF0YSBSZXNlYXJjaGVyXG4iLCAiYWdlbnRfa2V5IjogIjczYzM0OWM5
|
||||
M2MxNjNiNWQ0ZGY5OGE2NGZhYzFjNDMwIiwgInRvb2xzX25hbWVzIjogW119LCB7ImtleSI6ICJi
|
||||
MTdiMTg4ZGJmMTRmOTNhOThlNWI5NWFhZDM2NzU3NyIsICJpZCI6ICIwMGJmZDY5ZC03OWZiLTRj
|
||||
MjctYTM0Yi02NzBkZWJlMzU0NWYiLCAiYXN5bmNfZXhlY3V0aW9uPyI6IGZhbHNlLCAiaHVtYW5f
|
||||
aW5wdXQ/IjogZmFsc2UsICJhZ2VudF9yb2xlIjogInt0b3BpY30gUmVwb3J0aW5nIEFuYWx5c3Rc
|
||||
biIsICJhZ2VudF9rZXkiOiAiYmIwNjgzNzdjMTY0MWJlNmQ3ZDk3YTUxNjU5ZGI2MTMiLCAidG9v
|
||||
bHNfbmFtZXMiOiBbXX1degIYAYUBAAEAABKOAgoQsN5cQC9ZzBr2B0OKBR2WCxII3ULL7Wk965Yq
|
||||
DFRhc2sgQ3JlYXRlZDABOWB9ROHWrwgYQfg0ReHWrwgYSi4KCGNyZXdfa2V5EiIKIGYzNDZhOWFk
|
||||
NmQ3MzA2M2UwNjc3YjE3Y2U5YzUwMTc3SjEKB2NyZXdfaWQSJgokNzY0Y2VjNWEtODc5MS00Zjdj
|
||||
LTlmNDAtYTUzMzMyZjk5N2MwSi4KCHRhc2tfa2V5EiIKIDZhZmM0YjM5NjI1OWZiYjc2ODFmNTZj
|
||||
Nzc1NWNjOTM3SjEKB3Rhc2tfaWQSJgokNGY1MWVjMzYtNWUwNy00ZTg2LWJjMjEtZTUxNDQzODZi
|
||||
YjJhegIYAYUBAAEAAA==
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- '1894'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
User-Agent:
|
||||
- OTel-OTLP-Exporter-Python/1.27.0
|
||||
method: POST
|
||||
uri: https://telemetry.crewai.com:4319/v1/traces
|
||||
response:
|
||||
body:
|
||||
string: "\n\0"
|
||||
headers:
|
||||
Content-Length:
|
||||
- '2'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
Date:
|
||||
- Sun, 17 Nov 2024 07:09:57 GMT
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: '{"messages": [{"role": "system", "content": "You are LLMs Senior Data Researcher\n.
|
||||
You''re a seasoned researcher with a knack for uncovering the latest developments
|
||||
in LLMs. Known for your ability to find the most relevant information and present
|
||||
it in a clear and concise manner.\n\nYour personal goal is: Uncover cutting-edge
|
||||
developments in LLMs\n\nTo give my best complete final answer to the task use
|
||||
the exact following format:\n\nThought: I now can give a great answer\nFinal
|
||||
Answer: Your final answer must be the great and the most complete as possible,
|
||||
it must be outcome described.\n\nI MUST use these formats, my job depends on
|
||||
it!"}, {"role": "user", "content": "\nCurrent Task: Conduct a thorough research
|
||||
about LLMs Make sure you find any interesting and relevant information given
|
||||
the current year is 2024.\n\n\nThis is the expect criteria for your final answer:
|
||||
A list with 10 bullet points of the most relevant information about LLMs\n\nyou
|
||||
MUST return the actual complete content as the final answer, not a summary.\n\nBegin!
|
||||
This is VERY important to you, use the tools available and give your best Final
|
||||
Answer, your job depends on it!\n\nThought:"}], "model": "gpt-4o", "stop": ["\nObservation:"],
|
||||
"stream": false}'
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1235'
|
||||
content-type:
|
||||
- application/json
|
||||
cookie:
|
||||
- __cf_bm=08pKRcLhS1PDw0mYfL2jz19ac6M.T31GoiMuI5DlX6w-1731827382-1.0.1.1-UfOLu3AaIUuXP1sGzdV6oggJ1q7iMTC46t08FDhYVrKcW5YmD4CbifudOJiSgx8h0JLTwZdgk.aG05S0eAO_PQ;
|
||||
_cfuvid=74kaPOoAcp8YRSA0XocQ1FFNksu9V0_KiWdQfo7wQuQ-1731827382509-0.0.1.1-604800000
|
||||
host:
|
||||
- api.openai.com
|
||||
user-agent:
|
||||
- OpenAI/Python 1.52.1
|
||||
x-stainless-arch:
|
||||
- arm64
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- MacOS
|
||||
x-stainless-package-version:
|
||||
- 1.52.1
|
||||
x-stainless-raw-response:
|
||||
- 'true'
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.11.7
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
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
|
||||
AhiZyAcAAP//AwCidyXxtw8AAA==
|
||||
headers:
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 8e3de5de7b6c6217-GRU
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Sun, 17 Nov 2024 07:10:00 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
access-control-expose-headers:
|
||||
- X-Request-ID
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
openai-organization:
|
||||
- crewai-iuxna1
|
||||
openai-processing-ms:
|
||||
- '6026'
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
strict-transport-security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
x-ratelimit-limit-requests:
|
||||
- '10000'
|
||||
x-ratelimit-limit-tokens:
|
||||
- '30000000'
|
||||
x-ratelimit-remaining-requests:
|
||||
- '9999'
|
||||
x-ratelimit-remaining-tokens:
|
||||
- '29999713'
|
||||
x-ratelimit-reset-requests:
|
||||
- 6ms
|
||||
x-ratelimit-reset-tokens:
|
||||
- 0s
|
||||
x-request-id:
|
||||
- req_553f04a622d026a28dd3c5da55568fcd
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: !!binary |
|
||||
Cs4CCiQKIgoMc2VydmljZS5uYW1lEhIKEGNyZXdBSS10ZWxlbWV0cnkSpQIKEgoQY3Jld2FpLnRl
|
||||
bGVtZXRyeRKOAgoQIn+FuHJydyMnR3y/Qfb8GBII2zXFs4gynEgqDFRhc2sgQ3JlYXRlZDABOShs
|
||||
9ljYrwgYQQiP+FjYrwgYSi4KCGNyZXdfa2V5EiIKIGYzNDZhOWFkNmQ3MzA2M2UwNjc3YjE3Y2U5
|
||||
YzUwMTc3SjEKB2NyZXdfaWQSJgokNzY0Y2VjNWEtODc5MS00ZjdjLTlmNDAtYTUzMzMyZjk5N2Mw
|
||||
Si4KCHRhc2tfa2V5EiIKIGIxN2IxODhkYmYxNGY5M2E5OGU1Yjk1YWFkMzY3NTc3SjEKB3Rhc2tf
|
||||
aWQSJgokMDBiZmQ2OWQtNzlmYi00YzI3LWEzNGItNjcwZGViZTM1NDVmegIYAYUBAAEAAA==
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- '337'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
User-Agent:
|
||||
- OTel-OTLP-Exporter-Python/1.27.0
|
||||
method: POST
|
||||
uri: https://telemetry.crewai.com:4319/v1/traces
|
||||
response:
|
||||
body:
|
||||
string: "\n\0"
|
||||
headers:
|
||||
Content-Length:
|
||||
- '2'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
Date:
|
||||
- Sun, 17 Nov 2024 07:10:01 GMT
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: '{"messages": [{"role": "system", "content": "You are LLMs Reporting Analyst\n.
|
||||
You''re a meticulous analyst with a keen eye for detail. You''re known for your
|
||||
ability to turn complex data into clear and concise reports, making it easy
|
||||
for others to understand and act on the information you provide.\nYour personal
|
||||
goal is: Create detailed reports based on LLMs data analysis and research findings\n\nTo
|
||||
give my best complete final answer to the task use the exact following format:\n\nThought:
|
||||
I now can give a great answer\nFinal Answer: Your final answer must be the great
|
||||
and the most complete as possible, it must be outcome described.\n\nI MUST use
|
||||
these formats, my job depends on it!"}, {"role": "user", "content": "\nCurrent
|
||||
Task: Review the context you got and expand each topic into a full section for
|
||||
a report. Make sure the report is detailed and contains any and all relevant
|
||||
information.\n\n\nThis is the expect criteria for your final answer: A fully
|
||||
fledge reports with the mains topics, each with a full section of information.
|
||||
Formatted as markdown without ''```''\n\nyou MUST return the actual complete
|
||||
content as the final answer, not a summary.\n\nThis is the context you''re working
|
||||
with:\n1. **OpenAI''s GPT-4 Released**: OpenAI released GPT-4, which further
|
||||
improves contextual understanding and generation capabilities. It offers increased
|
||||
accuracy, creativity, and coherence in responses compared to its predecessors,
|
||||
making it an essential tool for businesses, educators, and developers.\n\n2.
|
||||
**Google''s Gemini Model**: In 2024, Google launched the Gemini model, focusing
|
||||
on merging language understanding with multimodal capabilities. This model can
|
||||
process text, audio, and images simultaneously, enhancing its applications in
|
||||
fields like voice assistants and image captioning.\n\n3. **Anthropic''s Claude**:
|
||||
Claude, by Anthropic, is designed to prioritize safety and ethical considerations
|
||||
in LLM usage. It''s built to minimize harmful outputs and biases prevalent in
|
||||
earlier language models, demonstrating a shift towards responsible AI deployment.\n\n4.
|
||||
**Meta''s Open Pre-trained Transformer (OPT) 3.0**: Meta has introduced OPT
|
||||
3.0, boasting efficient training approaches that lower computational costs while
|
||||
maintaining high performance. The model excels in translation tasks and has
|
||||
become a popular choice for academic research due to its open-access policy.\n\n5.
|
||||
**Language Model Distillation Advances**: Recent advances in model distillation
|
||||
techniques have allowed developers to deploy smaller, more efficient language
|
||||
models on edge devices without notably compromising performance, expanding the
|
||||
accessibility and application of LLMs in mobile and IoT devices.\n\n6. **Fine-Tuning
|
||||
with Limited Data**: Methods for fine-tuning LLMs with limited data have improved,
|
||||
enabling customization for niche applications without the need for vast datasets.
|
||||
This development has opened doors to using LLMs in specialized industries, like
|
||||
legal and medical sectors.\n\n7. **Ethical and Transparent AI Use**: 2024 has
|
||||
seen a significant emphasis on ethical AI, with organizations establishing standardized
|
||||
frameworks for LLM transparency and accountability. More companies are adopting
|
||||
practices like AI model cards to disclose model capabilities, limitations, and
|
||||
data sources.\n\n8. **The Rise of Prompt Engineering**: As models become more
|
||||
advanced, prompt engineering has emerged as a crucial technique for optimizing
|
||||
model outputs. This involves designing specific input prompts that guide LLMs
|
||||
to yield desired results, thus enhancing usability in content creation and customer
|
||||
service.\n\n9. **Integration with Augmented Reality**: Language models in 2024
|
||||
are increasingly being integrated with AR technologies to provide real-time
|
||||
language translation, virtual assistants in immersive environments, and interactive
|
||||
educational tools, enriching the user''s experience with contextualized AI assistance.\n\n10.
|
||||
**Regulatory Developments**: Governments worldwide are progressing towards formalizing
|
||||
regulations around LLM usage, focusing on data privacy, security, and ethical
|
||||
concerns. These developments aim to safeguard users while encouraging innovation
|
||||
in AI technology.\n\nThese points reflect the cutting-edge advancements and
|
||||
trends in the field of large language models (LLMs) as of 2024, highlighting
|
||||
their growing influence and the increasing focus on ethical and practical deployment.\n\nBegin!
|
||||
This is VERY important to you, use the tools available and give your best Final
|
||||
Answer, your job depends on it!\n\nThought:"}], "model": "gpt-4o", "stop": ["\nObservation:"],
|
||||
"stream": false}'
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '4624'
|
||||
content-type:
|
||||
- application/json
|
||||
cookie:
|
||||
- __cf_bm=08pKRcLhS1PDw0mYfL2jz19ac6M.T31GoiMuI5DlX6w-1731827382-1.0.1.1-UfOLu3AaIUuXP1sGzdV6oggJ1q7iMTC46t08FDhYVrKcW5YmD4CbifudOJiSgx8h0JLTwZdgk.aG05S0eAO_PQ;
|
||||
_cfuvid=74kaPOoAcp8YRSA0XocQ1FFNksu9V0_KiWdQfo7wQuQ-1731827382509-0.0.1.1-604800000
|
||||
host:
|
||||
- api.openai.com
|
||||
user-agent:
|
||||
- OpenAI/Python 1.52.1
|
||||
x-stainless-arch:
|
||||
- arm64
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- MacOS
|
||||
x-stainless-package-version:
|
||||
- 1.52.1
|
||||
x-stainless-raw-response:
|
||||
- 'true'
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.11.7
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
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
|
||||
AAAA//8DAMIhqlTfHQAA
|
||||
headers:
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 8e3de605dca06217-GRU
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Sun, 17 Nov 2024 07:10:10 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
access-control-expose-headers:
|
||||
- X-Request-ID
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
openai-organization:
|
||||
- crewai-iuxna1
|
||||
openai-processing-ms:
|
||||
- '9951'
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
strict-transport-security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
x-ratelimit-limit-requests:
|
||||
- '10000'
|
||||
x-ratelimit-limit-tokens:
|
||||
- '30000000'
|
||||
x-ratelimit-remaining-requests:
|
||||
- '9999'
|
||||
x-ratelimit-remaining-tokens:
|
||||
- '29998873'
|
||||
x-ratelimit-reset-requests:
|
||||
- 6ms
|
||||
x-ratelimit-reset-tokens:
|
||||
- 2ms
|
||||
x-request-id:
|
||||
- req_52a4f98f9c08fbaa1724634d237f3245
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
487
tests/cassettes/test_after_kickoff_modification.yaml
Normal file
487
tests/cassettes/test_after_kickoff_modification.yaml
Normal file
@@ -0,0 +1,487 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: !!binary |
|
||||
CusOCiQKIgoMc2VydmljZS5uYW1lEhIKEGNyZXdBSS10ZWxlbWV0cnkSwg4KEgoQY3Jld2FpLnRl
|
||||
bGVtZXRyeRKaDAoQJ2RtlOW3xhPcNjmbKwSJaxIIMUF8zJjQkvQqDENyZXcgQ3JlYXRlZDABOThF
|
||||
x7PrrgkYQWiczLPrrgkYShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuODAuMEoaCg5weXRob25fdmVy
|
||||
c2lvbhIICgYzLjEyLjdKLgoIY3Jld19rZXkSIgogMWYxMjhiZGI3YmFhNGI2NzcxNGYxZGFlZGMy
|
||||
ZjNhYjZKMQoHY3Jld19pZBImCiQzNGJiYzZjYS03MmRiLTQwMzktODQzMy01NTFmOWNmNDM0YTdK
|
||||
HAoMY3Jld19wcm9jZXNzEgwKCnNlcXVlbnRpYWxKEQoLY3Jld19tZW1vcnkSAhAAShoKFGNyZXdf
|
||||
bnVtYmVyX29mX3Rhc2tzEgIYAkobChVjcmV3X251bWJlcl9vZl9hZ2VudHMSAhgCSrQFCgtjcmV3
|
||||
X2FnZW50cxKkBQqhBVt7ImtleSI6ICI3M2MzNDljOTNjMTYzYjVkNGRmOThhNjRmYWMxYzQzMCIs
|
||||
ICJpZCI6ICI4MjJkOGM2OC01NzlkLTQ4ZWUtOTBhMi1hNjJiNDgzY2JhNGUiLCAicm9sZSI6ICJ7
|
||||
dG9waWN9IFNlbmlvciBEYXRhIFJlc2VhcmNoZXJcbiIsICJ2ZXJib3NlPyI6IHRydWUsICJtYXhf
|
||||
aXRlciI6IDIwLCAibWF4X3JwbSI6IG51bGwsICJmdW5jdGlvbl9jYWxsaW5nX2xsbSI6ICIiLCAi
|
||||
bGxtIjogImdwdC00by1taW5pIiwgImRlbGVnYXRpb25fZW5hYmxlZD8iOiBmYWxzZSwgImFsbG93
|
||||
X2NvZGVfZXhlY3V0aW9uPyI6IGZhbHNlLCAibWF4X3JldHJ5X2xpbWl0IjogMiwgInRvb2xzX25h
|
||||
bWVzIjogW119LCB7ImtleSI6ICIxMDRmZTA2NTllMTBiNDI2Y2Y4OGYwMjRmYjU3MTU1MyIsICJp
|
||||
ZCI6ICI0YTY4NDQwZi0xMjRkLTQ3YmEtYWEzNy1hZTZmMTI2NzlkMmIiLCAicm9sZSI6ICJ7dG9w
|
||||
aWN9IFJlcG9ydGluZyBBbmFseXN0XG4iLCAidmVyYm9zZT8iOiB0cnVlLCAibWF4X2l0ZXIiOiAy
|
||||
MCwgIm1heF9ycG0iOiBudWxsLCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwgImxsbSI6ICJn
|
||||
cHQtNG8tbWluaSIsICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogZmFsc2UsICJhbGxvd19jb2RlX2V4
|
||||
ZWN1dGlvbj8iOiBmYWxzZSwgIm1heF9yZXRyeV9saW1pdCI6IDIsICJ0b29sc19uYW1lcyI6IFtd
|
||||
fV1KkwQKCmNyZXdfdGFza3MShAQKgQRbeyJrZXkiOiAiNmFmYzRiMzk2MjU5ZmJiNzY4MWY1NmM3
|
||||
NzU1Y2M5MzciLCAiaWQiOiAiODE2YzI1ZDgtNDg3NC00MmMxLWJmNzEtODc2OTcxZDNmYmExIiwg
|
||||
ImFzeW5jX2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAiYWdlbnRf
|
||||
cm9sZSI6ICJ7dG9waWN9IFNlbmlvciBEYXRhIFJlc2VhcmNoZXJcbiIsICJhZ2VudF9rZXkiOiAi
|
||||
NzNjMzQ5YzkzYzE2M2I1ZDRkZjk4YTY0ZmFjMWM0MzAiLCAidG9vbHNfbmFtZXMiOiBbXX0sIHsi
|
||||
a2V5IjogImIxN2IxODhkYmYxNGY5M2E5OGU1Yjk1YWFkMzY3NTc3IiwgImlkIjogIjM4YzU1NTI5
|
||||
LTc2ODAtNDc5OS1iODdiLTFmMDY2NjE5MGU2NyIsICJhc3luY19leGVjdXRpb24/IjogZmFsc2Us
|
||||
ICJodW1hbl9pbnB1dD8iOiBmYWxzZSwgImFnZW50X3JvbGUiOiAie3RvcGljfSBSZXBvcnRpbmcg
|
||||
QW5hbHlzdFxuIiwgImFnZW50X2tleSI6ICIxMDRmZTA2NTllMTBiNDI2Y2Y4OGYwMjRmYjU3MTU1
|
||||
MyIsICJ0b29sc19uYW1lcyI6IFtdfV16AhgBhQEAAQAAEo4CChCo3E4xT/U6O20NrD4/Zkt6EggD
|
||||
/w74tbrrOCoMVGFzayBDcmVhdGVkMAE5SPTas+uuCRhB6IDbs+uuCRhKLgoIY3Jld19rZXkSIgog
|
||||
MWYxMjhiZGI3YmFhNGI2NzcxNGYxZGFlZGMyZjNhYjZKMQoHY3Jld19pZBImCiQzNGJiYzZjYS03
|
||||
MmRiLTQwMzktODQzMy01NTFmOWNmNDM0YTdKLgoIdGFza19rZXkSIgogNmFmYzRiMzk2MjU5ZmJi
|
||||
NzY4MWY1NmM3NzU1Y2M5MzdKMQoHdGFza19pZBImCiQ4MTZjMjVkOC00ODc0LTQyYzEtYmY3MS04
|
||||
NzY5NzFkM2ZiYTF6AhgBhQEAAQAA
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- '1902'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
User-Agent:
|
||||
- OTel-OTLP-Exporter-Python/1.27.0
|
||||
method: POST
|
||||
uri: https://telemetry.crewai.com:4319/v1/traces
|
||||
response:
|
||||
body:
|
||||
string: "\n\0"
|
||||
headers:
|
||||
Content-Length:
|
||||
- '2'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
Date:
|
||||
- Wed, 20 Nov 2024 13:04:24 GMT
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: '{"messages": [{"role": "system", "content": "You are Bicycles Senior Data
|
||||
Researcher\n. You''re a seasoned researcher with a knack for uncovering the
|
||||
latest developments in Bicycles. Known for your ability to find the most relevant
|
||||
information and present it in a clear and concise manner.\n\nYour personal goal
|
||||
is: Uncover cutting-edge developments in Bicycles\n\nTo give my best complete
|
||||
final answer to the task use the exact following format:\n\nThought: I now can
|
||||
give a great answer\nFinal Answer: Your final answer must be the great and the
|
||||
most complete as possible, it must be outcome described.\n\nI MUST use these
|
||||
formats, my job depends on it!"}, {"role": "user", "content": "\nCurrent Task:
|
||||
Conduct a thorough research about Bicycles Make sure you find any interesting
|
||||
and relevant information given the current year is 2024.\n\n\nThis is the expect
|
||||
criteria for your final answer: A list with 10 bullet points of the most relevant
|
||||
information about Bicycles\n\nyou MUST return the actual complete content as
|
||||
the final answer, not a summary.\n\nBegin! This is VERY important to you, use
|
||||
the tools available and give your best Final Answer, your job depends on it!\n\nThought:"}],
|
||||
"model": "gpt-4o-mini", "stop": ["\nObservation:"], "stream": false}'
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1260'
|
||||
content-type:
|
||||
- application/json
|
||||
cookie:
|
||||
- __cf_bm=CkK4UvBd9ukXvn50uJwGambJcz5zERAJfeXJ9xge6H4-1732107842-1.0.1.1-IOK2yVL3RlD75MgmnKzIEyE38HNknwn6I8BBJ1wjGz4jCTd0YWIBPnvUm9gB8D_zLlUA9G7p_wbrfyc4mO_Bmg;
|
||||
_cfuvid=MmeN9oHWrBLThkEJdaSFHBfWe95JvA8iFnnt7CC92tk-1732107842102-0.0.1.1-604800000
|
||||
host:
|
||||
- api.openai.com
|
||||
user-agent:
|
||||
- OpenAI/Python 1.52.1
|
||||
x-stainless-arch:
|
||||
- x64
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- MacOS
|
||||
x-stainless-package-version:
|
||||
- 1.52.1
|
||||
x-stainless-raw-response:
|
||||
- 'true'
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.12.7
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
content: "{\n \"id\": \"chatcmpl-AVefTnyhy126z54bX4Wq0TjWFUGJI\",\n \"object\":
|
||||
\"chat.completion\",\n \"created\": 1732107859,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n
|
||||
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||
\"assistant\",\n \"content\": \"I now can give a great answer. \\nFinal
|
||||
Answer: \\n\\n1. **E-Bike Boom**: Electric bikes (e-bikes) have seen a significant
|
||||
rise in popularity, with industry reports indicating a projected growth of 60%
|
||||
in sales compared to previous years. Many cities are paving bike lanes specifically
|
||||
designed for e-bikes to accommodate this surge.\\n\\n2. **Sustainability in
|
||||
Manufacturing**: Bicycle manufacturers are increasingly adopting sustainable
|
||||
practices, such as using recycled materials for frames and parts, and implementing
|
||||
environmentally friendly production processes. This shift is driven by consumer
|
||||
demand for greener products.\\n\\n3. **Smart Bicycles**: The integration of
|
||||
technology in bicycles has progressed with smart bikes featuring built-in GPS,
|
||||
automated gear shifting, and performance analytics. These innovations enhance
|
||||
the cycling experience and cater to data-driven enthusiasts.\\n\\n4. **Bike
|
||||
Sharing Programs**: Urban areas are continuing to expand bike-sharing programs,
|
||||
with some cities introducing electric bike options and introducing smartphone
|
||||
apps to streamline the renting process, increasing accessibility and convenience
|
||||
for riders.\\n\\n5. **Safety Innovations**: Advances in safety technology such
|
||||
as smart helmets that incorporate lights and indicators, anti-collision systems
|
||||
using sensor technology, and built-in communication systems to connect with
|
||||
smartphones are on the rise, aimed at reducing accidents.\\n\\n6. **Adventure
|
||||
Cycling Trends**: There is a growing popularity in adventure and gravel cycling,
|
||||
with more cyclists seeking off-road experiences. This has prompted manufacturers
|
||||
to develop dedicated bikes that cater to rugged terrains, with features such
|
||||
as wider tires and durable frames.\\n\\n7. **Customization and Personalization**:
|
||||
The market for customizable bicycles is expanding. Consumers are now able to
|
||||
choose colors, styles, and features that suit their personal preferences, leading
|
||||
to a more personalized cycling experience.\\n\\n8. **Communities and Events**:
|
||||
Cycling communities are thriving globally, with an increase in events such as
|
||||
group rides, competitive races, and festivals celebrating biking culture. This
|
||||
fosters social engagement and promotes cycling as a lifestyle.\\n\\n9. **Cargo
|
||||
Bikes for Urban Living**: The rise of cargo bikes, particularly in urban environments,
|
||||
allows for efficient transportation of goods, making them an appealing choice
|
||||
for small businesses and families. This trend is encouraged by city planners
|
||||
promoting cycling as an alternative to car deliveries.\\n\\n10. **Regulatory
|
||||
Changes**: Governments around the world are increasingly implementing policies
|
||||
to support cycling infrastructure, such as funding for bike lanes, subsidies
|
||||
for bicycle purchases, and stricter emissions standards for motor vehicles,
|
||||
making cycling a more attractive option for commuting.\\n\\nEach of these points
|
||||
represents the latest developments in the bicycle industry as we move through
|
||||
2024, highlighting advancements in technology, trends in user preferences, and
|
||||
a broader societal shift towards sustainability and health.\",\n \"refusal\":
|
||||
null\n },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n
|
||||
\ }\n ],\n \"usage\": {\n \"prompt_tokens\": 237,\n \"completion_tokens\":
|
||||
539,\n \"total_tokens\": 776,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
||||
0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": {\n
|
||||
\ \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"system_fingerprint\":
|
||||
\"fp_0705bf87c0\"\n}\n"
|
||||
headers:
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 8e58a5276a096225-GRU
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 20 Nov 2024 13:04:26 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
access-control-expose-headers:
|
||||
- X-Request-ID
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
openai-organization:
|
||||
- crewai-iuxna1
|
||||
openai-processing-ms:
|
||||
- '7355'
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
strict-transport-security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
x-ratelimit-limit-requests:
|
||||
- '30000'
|
||||
x-ratelimit-limit-tokens:
|
||||
- '150000000'
|
||||
x-ratelimit-remaining-requests:
|
||||
- '29999'
|
||||
x-ratelimit-remaining-tokens:
|
||||
- '149999708'
|
||||
x-ratelimit-reset-requests:
|
||||
- 2ms
|
||||
x-ratelimit-reset-tokens:
|
||||
- 0s
|
||||
x-request-id:
|
||||
- req_5536f2a242886d3949f0cdc1628b2996
|
||||
http_version: HTTP/1.1
|
||||
status_code: 200
|
||||
- request:
|
||||
body: !!binary |
|
||||
Cs4CCiQKIgoMc2VydmljZS5uYW1lEhIKEGNyZXdBSS10ZWxlbWV0cnkSpQIKEgoQY3Jld2FpLnRl
|
||||
bGVtZXRyeRKOAgoQpBIRwGH/fJtGJT1cIWsC5BIIM3YyJZEYUUgqDFRhc2sgQ3JlYXRlZDABOYgb
|
||||
lILtrgkYQZBnlYLtrgkYSi4KCGNyZXdfa2V5EiIKIDFmMTI4YmRiN2JhYTRiNjc3MTRmMWRhZWRj
|
||||
MmYzYWI2SjEKB2NyZXdfaWQSJgokMzRiYmM2Y2EtNzJkYi00MDM5LTg0MzMtNTUxZjljZjQzNGE3
|
||||
Si4KCHRhc2tfa2V5EiIKIGIxN2IxODhkYmYxNGY5M2E5OGU1Yjk1YWFkMzY3NTc3SjEKB3Rhc2tf
|
||||
aWQSJgokMzhjNTU1MjktNzY4MC00Nzk5LWI4N2ItMWYwNjY2MTkwZTY3egIYAYUBAAEAAA==
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- '337'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
User-Agent:
|
||||
- OTel-OTLP-Exporter-Python/1.27.0
|
||||
method: POST
|
||||
uri: https://telemetry.crewai.com:4319/v1/traces
|
||||
response:
|
||||
body:
|
||||
string: "\n\0"
|
||||
headers:
|
||||
Content-Length:
|
||||
- '2'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
Date:
|
||||
- Wed, 20 Nov 2024 13:04:29 GMT
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: '{"messages": [{"role": "system", "content": "You are Bicycles Reporting
|
||||
Analyst\n. You''re a meticulous analyst with a keen eye for detail. You''re
|
||||
known for your ability to turn complex data into clear and concise reports,
|
||||
making it easy for others to understand and act on the information you provide.\n\nYour
|
||||
personal goal is: Create detailed reports based on Bicycles data analysis and
|
||||
research findings\n\nTo give my best complete final answer to the task use the
|
||||
exact following format:\n\nThought: I now can give a great answer\nFinal Answer:
|
||||
Your final answer must be the great and the most complete as possible, it must
|
||||
be outcome described.\n\nI MUST use these formats, my job depends on it!"},
|
||||
{"role": "user", "content": "\nCurrent Task: Review the context you got and
|
||||
expand each topic into a full section for a report. Make sure the report is
|
||||
detailed and contains any and all relevant information.\n\n\nThis is the expect
|
||||
criteria for your final answer: A fully fledge reports with the mains topics,
|
||||
each with a full section of information. Formatted as markdown without ''```''\n\nyou
|
||||
MUST return the actual complete content as the final answer, not a summary.\n\nThis
|
||||
is the context you''re working with:\n1. **E-Bike Boom**: Electric bikes (e-bikes)
|
||||
have seen a significant rise in popularity, with industry reports indicating
|
||||
a projected growth of 60% in sales compared to previous years. Many cities are
|
||||
paving bike lanes specifically designed for e-bikes to accommodate this surge.\n\n2.
|
||||
**Sustainability in Manufacturing**: Bicycle manufacturers are increasingly
|
||||
adopting sustainable practices, such as using recycled materials for frames
|
||||
and parts, and implementing environmentally friendly production processes. This
|
||||
shift is driven by consumer demand for greener products.\n\n3. **Smart Bicycles**:
|
||||
The integration of technology in bicycles has progressed with smart bikes featuring
|
||||
built-in GPS, automated gear shifting, and performance analytics. These innovations
|
||||
enhance the cycling experience and cater to data-driven enthusiasts.\n\n4. **Bike
|
||||
Sharing Programs**: Urban areas are continuing to expand bike-sharing programs,
|
||||
with some cities introducing electric bike options and introducing smartphone
|
||||
apps to streamline the renting process, increasing accessibility and convenience
|
||||
for riders.\n\n5. **Safety Innovations**: Advances in safety technology such
|
||||
as smart helmets that incorporate lights and indicators, anti-collision systems
|
||||
using sensor technology, and built-in communication systems to connect with
|
||||
smartphones are on the rise, aimed at reducing accidents.\n\n6. **Adventure
|
||||
Cycling Trends**: There is a growing popularity in adventure and gravel cycling,
|
||||
with more cyclists seeking off-road experiences. This has prompted manufacturers
|
||||
to develop dedicated bikes that cater to rugged terrains, with features such
|
||||
as wider tires and durable frames.\n\n7. **Customization and Personalization**:
|
||||
The market for customizable bicycles is expanding. Consumers are now able to
|
||||
choose colors, styles, and features that suit their personal preferences, leading
|
||||
to a more personalized cycling experience.\n\n8. **Communities and Events**:
|
||||
Cycling communities are thriving globally, with an increase in events such as
|
||||
group rides, competitive races, and festivals celebrating biking culture. This
|
||||
fosters social engagement and promotes cycling as a lifestyle.\n\n9. **Cargo
|
||||
Bikes for Urban Living**: The rise of cargo bikes, particularly in urban environments,
|
||||
allows for efficient transportation of goods, making them an appealing choice
|
||||
for small businesses and families. This trend is encouraged by city planners
|
||||
promoting cycling as an alternative to car deliveries.\n\n10. **Regulatory Changes**:
|
||||
Governments around the world are increasingly implementing policies to support
|
||||
cycling infrastructure, such as funding for bike lanes, subsidies for bicycle
|
||||
purchases, and stricter emissions standards for motor vehicles, making cycling
|
||||
a more attractive option for commuting.\n\nEach of these points represents the
|
||||
latest developments in the bicycle industry as we move through 2024, highlighting
|
||||
advancements in technology, trends in user preferences, and a broader societal
|
||||
shift towards sustainability and health.\n\nBegin! This is VERY important to
|
||||
you, use the tools available and give your best Final Answer, your job depends
|
||||
on it!\n\nThought:"}], "model": "gpt-4o-mini", "stop": ["\nObservation:"], "stream":
|
||||
false}'
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '4457'
|
||||
content-type:
|
||||
- application/json
|
||||
cookie:
|
||||
- __cf_bm=CkK4UvBd9ukXvn50uJwGambJcz5zERAJfeXJ9xge6H4-1732107842-1.0.1.1-IOK2yVL3RlD75MgmnKzIEyE38HNknwn6I8BBJ1wjGz4jCTd0YWIBPnvUm9gB8D_zLlUA9G7p_wbrfyc4mO_Bmg;
|
||||
_cfuvid=MmeN9oHWrBLThkEJdaSFHBfWe95JvA8iFnnt7CC92tk-1732107842102-0.0.1.1-604800000
|
||||
host:
|
||||
- api.openai.com
|
||||
user-agent:
|
||||
- OpenAI/Python 1.52.1
|
||||
x-stainless-arch:
|
||||
- x64
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- MacOS
|
||||
x-stainless-package-version:
|
||||
- 1.52.1
|
||||
x-stainless-raw-response:
|
||||
- 'true'
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.12.7
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
content: "{\n \"id\": \"chatcmpl-AVefbpMLcvelEguI3pyXOpfbaXLGG\",\n \"object\":
|
||||
\"chat.completion\",\n \"created\": 1732107867,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n
|
||||
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||
\"assistant\",\n \"content\": \"I now can give a great answer \\nFinal
|
||||
Answer: \\n\\n# Comprehensive Report on the Latest Developments in the Bicycle
|
||||
Industry (2024)\\n\\n## 1. E-Bike Boom\\nThe popularity of electric bikes (e-bikes)
|
||||
has surged dramatically in recent years, with industry reports indicating a
|
||||
projected growth of 60% in sales compared to previous years. This growth can
|
||||
be attributed to increasing urbanization, the rising need for more sustainable
|
||||
modes of transport, and technological advancements that have made e-bikes more
|
||||
accessible and desirable. Cities worldwide are responding to this boom by developing
|
||||
dedicated bike lanes specifically designed for e-bikes, which not only promotes
|
||||
safety but also encourages more individuals to consider cycling as a primary
|
||||
mode of transportation.\\n\\n## 2. Sustainability in Manufacturing\\nIn line
|
||||
with global trends towards sustainability, bicycle manufacturers are increasingly
|
||||
adopting eco-friendlier practices. They are utilizing recycled materials for
|
||||
frames and components and implementing environmentally friendly production processes.
|
||||
This shift is not just a response to regulatory pressures but also driven by
|
||||
consumer demand for greener products. Companies that prioritize sustainability
|
||||
are seeing a competitive edge in an increasingly eco-conscious market, as consumers
|
||||
are more likely to align their purchases with their values regarding environmental
|
||||
responsibility.\\n\\n## 3. Smart Bicycles\\nThe integration of technology in
|
||||
bicycles has advanced significantly, resulting in the emergence of smart bikes.
|
||||
These bicycles often feature built-in GPS for navigation, automated gear shifting
|
||||
for smoother rides, and performance analytics that allow users to track their
|
||||
cycling metrics. Such innovations enhance the overall cycling experience and
|
||||
cater to performance-focused cyclists who seek data to optimize their rides.
|
||||
By merging cycling with technology, manufacturers are not only attracting tech
|
||||
enthusiasts but also making cycling more mainstream.\\n\\n## 4. Bike Sharing
|
||||
Programs\\nBike-sharing programs are rapidly expanding, particularly in urban
|
||||
areas. Many cities have started introducing electric bike options within these
|
||||
programs to meet the growing demand. The introduction of smartphone apps has
|
||||
streamlined the renting process, increasing accessibility and convenience for
|
||||
users. This trend not only promotes a healthier lifestyle but also reduces traffic
|
||||
congestion and environmental impact in densely populated areas, making cycling
|
||||
a more viable option for commuting.\\n\\n## 5. Safety Innovations\\nRecent advancements
|
||||
in safety technology are working towards making cycling safer. Innovations such
|
||||
as smart helmets equipped with lights and turn indicators, anti-collision systems
|
||||
utilizing sensor technology, and integrated communication systems linking bicycles
|
||||
with smartphones are increasingly gaining traction. These developments aim to
|
||||
minimize accidents and enhance the overall sense of security for cyclists, thereby
|
||||
encouraging more people to take up cycling as a daily activity.\\n\\n## 6. Adventure
|
||||
Cycling Trends\\nAdventure and gravel cycling are witnessing a renaissance,
|
||||
with many cyclists seeking off-road experiences that enable a connection with
|
||||
nature. This trend has led manufacturers to innovate by developing dedicated
|
||||
bikes suited for rugged terrains, characterized by features like wider tires
|
||||
and durable frames. As consumers become more adventurous in their hobbies, manufacturers
|
||||
are recognizing the need to cater to this niche market, fostering the growth
|
||||
of adventure cycling as a distinct segment in the industry.\\n\\n## 7. Customization
|
||||
and Personalization\\nThe demand for customizable bicycles is on the rise, allowing
|
||||
consumers to choose various aspects of their bikes, including colors, styles,
|
||||
and features. This trend towards personalization is enhancing the cycling experience,
|
||||
as riders can tailor their bicycles to their preferences. The flourishing market
|
||||
for custom bikes reflects a broader societal shift towards individuality and
|
||||
self-expression, as consumers are no longer content with one-size-fits-all solutions.\\n\\n##
|
||||
8. Communities and Events\\nCycling communities are thriving worldwide, reflected
|
||||
in an increase in events such as group rides, competitive races, and festivals
|
||||
celebrating biking culture. These gatherings not only foster a sense of camaraderie
|
||||
among cyclists but also promote cycling as a lifestyle choice to the wider community.
|
||||
The growth of these events is instrumental in building a culture around cycling,
|
||||
driving advocacy for cycling infrastructure and safety, and ultimately increasing
|
||||
the number of people who cycle.\\n\\n## 9. Cargo Bikes for Urban Living\\nThe
|
||||
rise of cargo bikes, especially in urban settings, represents an innovative
|
||||
solution for transporting goods efficiently while reducing reliance on motor
|
||||
vehicles. Such bikes serve as an appealing alternative for small businesses
|
||||
and families alike, allowing for easy deliveries and shopping. City planners
|
||||
are increasingly promoting cargo bikes within urban transport strategies, recognizing
|
||||
them as a sustainable option that aligns with broader goals for reducing carbon
|
||||
footprints and enhancing urban mobility.\\n\\n## 10. Regulatory Changes\\nGovernments
|
||||
around the globe are progressively enacting regulations to support and grow
|
||||
cycling infrastructure. Initiatives include funding for bike lanes, subsidies
|
||||
for bicycle purchases, and stricter emissions standards for cars. These regulatory
|
||||
changes are making cycling a more attractive option for commuting and are an
|
||||
acknowledgment of the role that cycling plays in reducing pollution and traffic
|
||||
congestion. Such policies are instrumental in fostering a cycling-friendly environment
|
||||
and encouraging more people to adopt biking as a daily mode of transportation.\\n\\nThis
|
||||
report highlights the most significant developments in the bicycle industry
|
||||
as we advance through 2024, showcasing the technological breakthroughs, shifts
|
||||
in user preferences, and an overarching movement toward sustainability and health.
|
||||
These trends are indicative of a vibrant cycling culture that continues to evolve
|
||||
to meet the needs of modern society.\",\n \"refusal\": null\n },\n
|
||||
\ \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n ],\n
|
||||
\ \"usage\": {\n \"prompt_tokens\": 790,\n \"completion_tokens\": 1022,\n
|
||||
\ \"total_tokens\": 1812,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
||||
0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": {\n
|
||||
\ \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"system_fingerprint\":
|
||||
\"fp_0705bf87c0\"\n}\n"
|
||||
headers:
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 8e58a5580add6225-GRU
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 20 Nov 2024 13:04:46 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
access-control-expose-headers:
|
||||
- X-Request-ID
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
openai-organization:
|
||||
- crewai-iuxna1
|
||||
openai-processing-ms:
|
||||
- '18921'
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
strict-transport-security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
x-ratelimit-limit-requests:
|
||||
- '30000'
|
||||
x-ratelimit-limit-tokens:
|
||||
- '150000000'
|
||||
x-ratelimit-remaining-requests:
|
||||
- '29999'
|
||||
x-ratelimit-remaining-tokens:
|
||||
- '149998916'
|
||||
x-ratelimit-reset-requests:
|
||||
- 2ms
|
||||
x-ratelimit-reset-tokens:
|
||||
- 0s
|
||||
x-request-id:
|
||||
- req_32b801874a2fed46b91251052364ec47
|
||||
http_version: HTTP/1.1
|
||||
status_code: 200
|
||||
version: 1
|
||||
115
tests/cassettes/test_agent_with_knowledge_sources.yaml
Normal file
115
tests/cassettes/test_agent_with_knowledge_sources.yaml
Normal file
@@ -0,0 +1,115 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: '{"messages": [{"role": "system", "content": "You are Information Agent.
|
||||
You have access to specific knowledge sources.\nYour personal goal is: Provide
|
||||
information based on knowledge sources\nTo give my best complete final answer
|
||||
to the task use the exact following format:\n\nThought: I now can give a great
|
||||
answer\nFinal Answer: Your final answer must be the great and the most complete
|
||||
as possible, it must be outcome described.\n\nI MUST use these formats, my job
|
||||
depends on it!"}, {"role": "user", "content": "\nCurrent Task: What is Brandon''s
|
||||
favorite color?\n\nThis is the expect criteria for your final answer: Brandon''s
|
||||
favorite color.\nyou MUST return the actual complete content as the final answer,
|
||||
not a summary.\n\nBegin! This is VERY important to you, use the tools available
|
||||
and give your best Final Answer, your job depends on it!\n\nThought:"}], "model":
|
||||
"gpt-4o-mini", "stop": ["\nObservation:"], "stream": false}'
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '931'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- api.openai.com
|
||||
user-agent:
|
||||
- OpenAI/Python 1.52.1
|
||||
x-stainless-arch:
|
||||
- arm64
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- MacOS
|
||||
x-stainless-package-version:
|
||||
- 1.52.1
|
||||
x-stainless-raw-response:
|
||||
- 'true'
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.11.9
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAAA4xSQW7bMBC86xULXnqxAtmxI1e3FEWBtJekCXJpC4GmVhIdapcgqbhN4L8HlB1L
|
||||
QVOgFwGa2RnOLPmcAAhdiQKEamVQnTXp5f3d9lsdbndh++C+757Or6/bm6uvn59WH/FGzKKCN1tU
|
||||
4VV1prizBoNmOtDKoQwYXef5+SK7WK3ny4HouEITZY0N6ZLTTpNOF9limWZ5Ol8f1S1rhV4U8CMB
|
||||
AHgevjEnVfhbFJDNXpEOvZcNiuI0BCAcm4gI6b32QVIQs5FUTAFpiH4FxDtQkqDRjwgSmhgbJPkd
|
||||
OoCf9EWTNHA5/BfwyUmqmD54qOUjOx0QFBt2oD1sTI9n02Mc1r2XsSr1xhzx/Sm34cY63vgjf8Jr
|
||||
Tdq3pUPpmWJGH9iKgd0nAL+G/fRvKgvruLOhDPyAFA3nF/nBT4zXMmHXRzJwkGaKr2bv+JUVBqmN
|
||||
n2xYKKlarEbpeB2yrzRPiGTS+u8073kfmmtq/sd+JJRCG7AqrcNKq7eNxzGH8dX+a+y05SGw8H98
|
||||
wK6sNTXorNOHN1PbMsuz1aZe5yoTyT55AQAA//8DAPaYLdRBAwAA
|
||||
headers:
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 8e54a2a7d81467f7-SJC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 20 Nov 2024 01:23:34 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- __cf_bm=DoHo1Z11nN9bxkwZmJGnaxRhyrWE0UfyimYuUVRU6A4-1732065814-1.0.1.1-JVRvFrIJLHEq9OaFQS0qcgYcawE7t2XQ4Tpqd58n2Yfx3mvEqD34MJmooi1LtvdvjB2J8x1Rs.rCdXD.msLlKw;
|
||||
path=/; expires=Wed, 20-Nov-24 01:53:34 GMT; domain=.api.openai.com; HttpOnly;
|
||||
Secure; SameSite=None
|
||||
- _cfuvid=n3RrNhFMqC3HtJ7n3e3agyxnM1YOQ6eKESz_eeXLtZA-1732065814630-0.0.1.1-604800000;
|
||||
path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
access-control-expose-headers:
|
||||
- X-Request-ID
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
openai-organization:
|
||||
- crewai-iuxna1
|
||||
openai-processing-ms:
|
||||
- '344'
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
strict-transport-security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
x-ratelimit-limit-requests:
|
||||
- '30000'
|
||||
x-ratelimit-limit-tokens:
|
||||
- '150000000'
|
||||
x-ratelimit-remaining-requests:
|
||||
- '29999'
|
||||
x-ratelimit-remaining-tokens:
|
||||
- '149999790'
|
||||
x-ratelimit-reset-requests:
|
||||
- 2ms
|
||||
x-ratelimit-reset-tokens:
|
||||
- 0s
|
||||
x-request-id:
|
||||
- req_8f1622677c64913753a595f679596614
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
445
tests/cassettes/test_before_crew_modification.yaml
Normal file
445
tests/cassettes/test_before_crew_modification.yaml
Normal file
@@ -0,0 +1,445 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: '{"messages": [{"role": "system", "content": "You are Bicycles Senior Data
|
||||
Researcher\n. You''re a seasoned researcher with a knack for uncovering the
|
||||
latest developments in Bicycles. Known for your ability to find the most relevant
|
||||
information and present it in a clear and concise manner.\n\nYour personal goal
|
||||
is: Uncover cutting-edge developments in Bicycles\n\nTo give my best complete
|
||||
final answer to the task use the exact following format:\n\nThought: I now can
|
||||
give a great answer\nFinal Answer: Your final answer must be the great and the
|
||||
most complete as possible, it must be outcome described.\n\nI MUST use these
|
||||
formats, my job depends on it!"}, {"role": "user", "content": "\nCurrent Task:
|
||||
Conduct a thorough research about Bicycles Make sure you find any interesting
|
||||
and relevant information given the current year is 2024.\n\n\nThis is the expect
|
||||
criteria for your final answer: A list with 10 bullet points of the most relevant
|
||||
information about Bicycles\n\nyou MUST return the actual complete content as
|
||||
the final answer, not a summary.\n\nBegin! This is VERY important to you, use
|
||||
the tools available and give your best Final Answer, your job depends on it!\n\nThought:"}],
|
||||
"model": "gpt-4o", "stop": ["\nObservation:"], "stream": false}'
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1255'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- api.openai.com
|
||||
user-agent:
|
||||
- OpenAI/Python 1.52.1
|
||||
x-stainless-arch:
|
||||
- arm64
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- MacOS
|
||||
x-stainless-package-version:
|
||||
- 1.52.1
|
||||
x-stainless-raw-response:
|
||||
- 'true'
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.11.7
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
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
|
||||
Ag4AAA==
|
||||
headers:
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 8e44d29989a61ab0-GRU
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Mon, 18 Nov 2024 03:20:10 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- __cf_bm=elWqsM.3Jt5.vyzDrpCmVftKrlxb0_fRVMZxBGUYfcE-1731900010-1.0.1.1-AxUZI4aRPPnqgUcewvytSN0TcEpcfBqYEZ.h2A96g3wUsy6Ui_pr4y81nyHf2Pcn1S3lz4zSmufsGDmnNKtHDQ;
|
||||
path=/; expires=Mon, 18-Nov-24 03:50:10 GMT; domain=.api.openai.com; HttpOnly;
|
||||
Secure; SameSite=None
|
||||
- _cfuvid=lzrs54cKet3l28qlaoF9_vtIs55.7H9Sbr6IhTssBmk-1731900010790-0.0.1.1-604800000;
|
||||
path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
access-control-expose-headers:
|
||||
- X-Request-ID
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
openai-organization:
|
||||
- crewai-iuxna1
|
||||
openai-processing-ms:
|
||||
- '5249'
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
strict-transport-security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
x-ratelimit-limit-requests:
|
||||
- '10000'
|
||||
x-ratelimit-limit-tokens:
|
||||
- '30000000'
|
||||
x-ratelimit-remaining-requests:
|
||||
- '9999'
|
||||
x-ratelimit-remaining-tokens:
|
||||
- '29999708'
|
||||
x-ratelimit-reset-requests:
|
||||
- 6ms
|
||||
x-ratelimit-reset-tokens:
|
||||
- 0s
|
||||
x-request-id:
|
||||
- req_a9781a68655042f161d8089cc3819728
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: !!binary |
|
||||
CuEOCiQKIgoMc2VydmljZS5uYW1lEhIKEGNyZXdBSS10ZWxlbWV0cnkSuA4KEgoQY3Jld2FpLnRl
|
||||
bGVtZXRyeRKQDAoQ2G6ncUKutPIsOmTplHC6bBIIclCMqGiNvUoqDENyZXcgQ3JlYXRlZDABOcBv
|
||||
KPXg8QgYQXAFLvXg8QgYShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuODAuMEoaCg5weXRob25fdmVy
|
||||
c2lvbhIICgYzLjExLjdKLgoIY3Jld19rZXkSIgogMWYxMjhiZGI3YmFhNGI2NzcxNGYxZGFlZGMy
|
||||
ZjNhYjZKMQoHY3Jld19pZBImCiQzNDEwYmI2Mi01NzYxLTRhMGQtOGY1Zi1hOTliZWY5NDYxM2VK
|
||||
HAoMY3Jld19wcm9jZXNzEgwKCnNlcXVlbnRpYWxKEQoLY3Jld19tZW1vcnkSAhAAShoKFGNyZXdf
|
||||
bnVtYmVyX29mX3Rhc2tzEgIYAkobChVjcmV3X251bWJlcl9vZl9hZ2VudHMSAhgCSqoFCgtjcmV3
|
||||
X2FnZW50cxKaBQqXBVt7ImtleSI6ICI3M2MzNDljOTNjMTYzYjVkNGRmOThhNjRmYWMxYzQzMCIs
|
||||
ICJpZCI6ICI1YzgyZGRkOS1kMTM3LTQ3MDMtODY0My1iNTFmZDBlMTUxMjkiLCAicm9sZSI6ICJ7
|
||||
dG9waWN9IFNlbmlvciBEYXRhIFJlc2VhcmNoZXJcbiIsICJ2ZXJib3NlPyI6IHRydWUsICJtYXhf
|
||||
aXRlciI6IDIwLCAibWF4X3JwbSI6IG51bGwsICJmdW5jdGlvbl9jYWxsaW5nX2xsbSI6ICIiLCAi
|
||||
bGxtIjogImdwdC00byIsICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogZmFsc2UsICJhbGxvd19jb2Rl
|
||||
X2V4ZWN1dGlvbj8iOiBmYWxzZSwgIm1heF9yZXRyeV9saW1pdCI6IDIsICJ0b29sc19uYW1lcyI6
|
||||
IFtdfSwgeyJrZXkiOiAiMTA0ZmUwNjU5ZTEwYjQyNmNmODhmMDI0ZmI1NzE1NTMiLCAiaWQiOiAi
|
||||
ODdlYmRiYTMtNDRmZS00ODBmLWI2MWQtMWYzZjIyMWE5MDE2IiwgInJvbGUiOiAie3RvcGljfSBS
|
||||
ZXBvcnRpbmcgQW5hbHlzdFxuIiwgInZlcmJvc2U/IjogdHJ1ZSwgIm1heF9pdGVyIjogMjAsICJt
|
||||
YXhfcnBtIjogbnVsbCwgImZ1bmN0aW9uX2NhbGxpbmdfbGxtIjogIiIsICJsbG0iOiAiZ3B0LTRv
|
||||
IiwgImRlbGVnYXRpb25fZW5hYmxlZD8iOiBmYWxzZSwgImFsbG93X2NvZGVfZXhlY3V0aW9uPyI6
|
||||
IGZhbHNlLCAibWF4X3JldHJ5X2xpbWl0IjogMiwgInRvb2xzX25hbWVzIjogW119XUqTBAoKY3Jl
|
||||
d190YXNrcxKEBAqBBFt7ImtleSI6ICI2YWZjNGIzOTYyNTlmYmI3NjgxZjU2Yzc3NTVjYzkzNyIs
|
||||
ICJpZCI6ICI2ZTIzZmMzMS02OGI2LTRjZTMtODZjNC0zMDcxZGUwZDdjMWIiLCAiYXN5bmNfZXhl
|
||||
Y3V0aW9uPyI6IGZhbHNlLCAiaHVtYW5faW5wdXQ/IjogZmFsc2UsICJhZ2VudF9yb2xlIjogInt0
|
||||
b3BpY30gU2VuaW9yIERhdGEgUmVzZWFyY2hlclxuIiwgImFnZW50X2tleSI6ICI3M2MzNDljOTNj
|
||||
MTYzYjVkNGRmOThhNjRmYWMxYzQzMCIsICJ0b29sc19uYW1lcyI6IFtdfSwgeyJrZXkiOiAiYjE3
|
||||
YjE4OGRiZjE0ZjkzYTk4ZTViOTVhYWQzNjc1NzciLCAiaWQiOiAiNzRhOWVhMjMtNzVmYy00NWFi
|
||||
LWIyMDAtMTllZTk0ZjU0Y2JkIiwgImFzeW5jX2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lu
|
||||
cHV0PyI6IGZhbHNlLCAiYWdlbnRfcm9sZSI6ICJ7dG9waWN9IFJlcG9ydGluZyBBbmFseXN0XG4i
|
||||
LCAiYWdlbnRfa2V5IjogIjEwNGZlMDY1OWUxMGI0MjZjZjg4ZjAyNGZiNTcxNTUzIiwgInRvb2xz
|
||||
X25hbWVzIjogW119XXoCGAGFAQABAAASjgIKEDkgSkh9vBYObKyMriyidxwSCG3RsAoOYBU/KgxU
|
||||
YXNrIENyZWF0ZWQwATkgjkr14PEIGEHYFkv14PEIGEouCghjcmV3X2tleRIiCiAxZjEyOGJkYjdi
|
||||
YWE0YjY3NzE0ZjFkYWVkYzJmM2FiNkoxCgdjcmV3X2lkEiYKJDM0MTBiYjYyLTU3NjEtNGEwZC04
|
||||
ZjVmLWE5OWJlZjk0NjEzZUouCgh0YXNrX2tleRIiCiA2YWZjNGIzOTYyNTlmYmI3NjgxZjU2Yzc3
|
||||
NTVjYzkzN0oxCgd0YXNrX2lkEiYKJDZlMjNmYzMxLTY4YjYtNGNlMy04NmM0LTMwNzFkZTBkN2Mx
|
||||
YnoCGAGFAQABAAA=
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- '1892'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
User-Agent:
|
||||
- OTel-OTLP-Exporter-Python/1.27.0
|
||||
method: POST
|
||||
uri: https://telemetry.crewai.com:4319/v1/traces
|
||||
response:
|
||||
body:
|
||||
string: "\n\0"
|
||||
headers:
|
||||
Content-Length:
|
||||
- '2'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
Date:
|
||||
- Mon, 18 Nov 2024 03:20:10 GMT
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: !!binary |
|
||||
Cs4CCiQKIgoMc2VydmljZS5uYW1lEhIKEGNyZXdBSS10ZWxlbWV0cnkSpQIKEgoQY3Jld2FpLnRl
|
||||
bGVtZXRyeRKOAgoQwB8k3adY9mK031pcBVZJKhII3fxizKFNiGkqDFRhc2sgQ3JlYXRlZDABOaj7
|
||||
K0Xi8QgYQUiCLUXi8QgYSi4KCGNyZXdfa2V5EiIKIDFmMTI4YmRiN2JhYTRiNjc3MTRmMWRhZWRj
|
||||
MmYzYWI2SjEKB2NyZXdfaWQSJgokMzQxMGJiNjItNTc2MS00YTBkLThmNWYtYTk5YmVmOTQ2MTNl
|
||||
Si4KCHRhc2tfa2V5EiIKIGIxN2IxODhkYmYxNGY5M2E5OGU1Yjk1YWFkMzY3NTc3SjEKB3Rhc2tf
|
||||
aWQSJgokNzRhOWVhMjMtNzVmYy00NWFiLWIyMDAtMTllZTk0ZjU0Y2JkegIYAYUBAAEAAA==
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- '337'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
User-Agent:
|
||||
- OTel-OTLP-Exporter-Python/1.27.0
|
||||
method: POST
|
||||
uri: https://telemetry.crewai.com:4319/v1/traces
|
||||
response:
|
||||
body:
|
||||
string: "\n\0"
|
||||
headers:
|
||||
Content-Length:
|
||||
- '2'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
Date:
|
||||
- Mon, 18 Nov 2024 03:20:15 GMT
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: '{"messages": [{"role": "system", "content": "You are Bicycles Reporting
|
||||
Analyst\n. You''re a meticulous analyst with a keen eye for detail. You''re
|
||||
known for your ability to turn complex data into clear and concise reports,
|
||||
making it easy for others to understand and act on the information you provide.\n\nYour
|
||||
personal goal is: Create detailed reports based on Bicycles data analysis and
|
||||
research findings\n\nTo give my best complete final answer to the task use the
|
||||
exact following format:\n\nThought: I now can give a great answer\nFinal Answer:
|
||||
Your final answer must be the great and the most complete as possible, it must
|
||||
be outcome described.\n\nI MUST use these formats, my job depends on it!"},
|
||||
{"role": "user", "content": "\nCurrent Task: Review the context you got and
|
||||
expand each topic into a full section for a report. Make sure the report is
|
||||
detailed and contains any and all relevant information.\n\n\nThis is the expect
|
||||
criteria for your final answer: A fully fledge reports with the mains topics,
|
||||
each with a full section of information. Formatted as markdown without ''```''\n\nyou
|
||||
MUST return the actual complete content as the final answer, not a summary.\n\nThis
|
||||
is the context you''re working with:\n1. **Electric Bicycles (E-Bikes) Dominate
|
||||
the Market:** In 2024, e-bikes continue to lead in sales growth globally. Their
|
||||
popularity is fueled by the advancement in battery technology, offering longer
|
||||
ranges and shorter charging times, making commuting more efficient and sustainable
|
||||
in urban environments.\n\n2. **Integration with Smart Technology:** Bicycle
|
||||
manufacturers are increasingly incorporating IoT technology to enhance user
|
||||
experience. Features like GPS tracking, fitness data logging, and anti-theft
|
||||
systems directly linked to smartphones are becoming standard in newer models.\n\n3.
|
||||
**Sustainable Manufacturing Techniques:** Environmental concerns have pushed
|
||||
companies to adopt greener manufacturing processes, such as utilizing recycled
|
||||
materials, reducing carbon footprints in production, and implementing circular
|
||||
economies within the bicycle industry.\n\n4. **Innovations in Lightweight Materials:**
|
||||
The development of new composite materials, including carbon and graphene, results
|
||||
in extremely lightweight and durable frames. This advancement is particularly
|
||||
noticeable in racing and mountain bikes, enhancing performance and speed.\n\n5.
|
||||
**Customizable and Modular Bike Designs:** In 2024, there is a notable trend
|
||||
toward bikes with modular designs that allow riders to customize parts and accessories
|
||||
easily. This trend caters to diverse consumer needs and promotes longer bike
|
||||
life cycles by allowing for parts replacement instead of whole bikes.\n\n6.
|
||||
**Expansion of Urban Cycling Infrastructure:** More cities worldwide are investing
|
||||
in cycling-friendly infrastructure, such as dedicated bike lanes and bike-sharing
|
||||
schemes, to encourage eco-friendly commuting and reduce traffic congestion.\n\n7.
|
||||
**Health and Wellness Benefits:** With growing awareness of health and fitness,
|
||||
more people are choosing cycling as a daily exercise routine. The industry sees
|
||||
a surge in sales of fitness-oriented bicycles designed to maximize cardiovascular
|
||||
and strength training benefits.\n\n8. **Rise of Cargo and Utility Bicycles:**
|
||||
There is an increase in demand for cargo bicycles, which are used for transporting
|
||||
goods over short distances, reflecting a shift towards sustainable business
|
||||
delivery options, particularly in urban settings.\n\n9. **Competitive Cycling
|
||||
and Esports:** Competitive cycling has embraced digital platforms, with virtual
|
||||
reality and augmented reality races gaining traction among cycling enthusiasts
|
||||
and professional athletes for training and competition purposes.\n\n10. **Focus
|
||||
on Bike Safety Innovations:** Advances in bicycle safety technology, including
|
||||
smart helmets with built-in communication systems and advanced lighting for
|
||||
night visibility, are considerably improving rider security, making cycling
|
||||
a safer mode of transport.\n\nBegin! This is VERY important to you, use the
|
||||
tools available and give your best Final Answer, your job depends on it!\n\nThought:"}],
|
||||
"model": "gpt-4o", "stop": ["\nObservation:"], "stream": false}'
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '4197'
|
||||
content-type:
|
||||
- application/json
|
||||
cookie:
|
||||
- __cf_bm=elWqsM.3Jt5.vyzDrpCmVftKrlxb0_fRVMZxBGUYfcE-1731900010-1.0.1.1-AxUZI4aRPPnqgUcewvytSN0TcEpcfBqYEZ.h2A96g3wUsy6Ui_pr4y81nyHf2Pcn1S3lz4zSmufsGDmnNKtHDQ;
|
||||
_cfuvid=lzrs54cKet3l28qlaoF9_vtIs55.7H9Sbr6IhTssBmk-1731900010790-0.0.1.1-604800000
|
||||
host:
|
||||
- api.openai.com
|
||||
user-agent:
|
||||
- OpenAI/Python 1.52.1
|
||||
x-stainless-arch:
|
||||
- arm64
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- MacOS
|
||||
x-stainless-package-version:
|
||||
- 1.52.1
|
||||
x-stainless-raw-response:
|
||||
- 'true'
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.11.7
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
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
|
||||
headers:
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 8e44d2bc2bec1ab0-GRU
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Mon, 18 Nov 2024 03:20:25 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
access-control-expose-headers:
|
||||
- X-Request-ID
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
openai-organization:
|
||||
- crewai-iuxna1
|
||||
openai-processing-ms:
|
||||
- '13936'
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
strict-transport-security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
x-ratelimit-limit-requests:
|
||||
- '10000'
|
||||
x-ratelimit-limit-tokens:
|
||||
- '30000000'
|
||||
x-ratelimit-remaining-requests:
|
||||
- '9999'
|
||||
x-ratelimit-remaining-tokens:
|
||||
- '29998979'
|
||||
x-ratelimit-reset-requests:
|
||||
- 6ms
|
||||
x-ratelimit-reset-tokens:
|
||||
- 2ms
|
||||
x-request-id:
|
||||
- req_602e9ec1c4bc0da2fdb284f809c50872
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
438
tests/cassettes/test_before_crew_with_none_input.yaml
Normal file
438
tests/cassettes/test_before_crew_with_none_input.yaml
Normal file
@@ -0,0 +1,438 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: !!binary |
|
||||
CuMOCiQKIgoMc2VydmljZS5uYW1lEhIKEGNyZXdBSS10ZWxlbWV0cnkSug4KEgoQY3Jld2FpLnRl
|
||||
bGVtZXRyeRKSDAoQf/zeqxfqyNP5BgW6rZrC0BIIiXyYjb3bUBcqDENyZXcgQ3JlYXRlZDABOXha
|
||||
vrnarwgYQcCbxrnarwgYShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuODAuMEoaCg5weXRob25fdmVy
|
||||
c2lvbhIICgYzLjExLjdKLgoIY3Jld19rZXkSIgogZjM0NmE5YWQ2ZDczMDYzZTA2NzdiMTdjZTlj
|
||||
NTAxNzdKMQoHY3Jld19pZBImCiQ2Yzg5NDczNy0zNWJjLTRhZDEtYjE2Ni1hZTY3ODhhMTA4YWZK
|
||||
HAoMY3Jld19wcm9jZXNzEgwKCnNlcXVlbnRpYWxKEQoLY3Jld19tZW1vcnkSAhAAShoKFGNyZXdf
|
||||
bnVtYmVyX29mX3Rhc2tzEgIYAkobChVjcmV3X251bWJlcl9vZl9hZ2VudHMSAhgCSqwFCgtjcmV3
|
||||
X2FnZW50cxKcBQqZBVt7ImtleSI6ICI3M2MzNDljOTNjMTYzYjVkNGRmOThhNjRmYWMxYzQzMCIs
|
||||
ICJpZCI6ICIzNDQ2YWRlOS05YWM0LTQ1NTUtOTlkNS0zYWM0MzdhMmMxNmUiLCAicm9sZSI6ICJ7
|
||||
dG9waWN9IFNlbmlvciBEYXRhIFJlc2VhcmNoZXJcbiIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4
|
||||
X2l0ZXIiOiAyMCwgIm1heF9ycG0iOiBudWxsLCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwg
|
||||
ImxsbSI6ICJncHQtNG8iLCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNlLCAiYWxsb3dfY29k
|
||||
ZV9leGVjdXRpb24/IjogZmFsc2UsICJtYXhfcmV0cnlfbGltaXQiOiAyLCAidG9vbHNfbmFtZXMi
|
||||
OiBbXX0sIHsia2V5IjogImJiMDY4Mzc3YzE2NDFiZTZkN2Q5N2E1MTY1OWRiNjEzIiwgImlkIjog
|
||||
IjExMzVjODkzLTRlZGUtNDRiNC1hMjZmLTIxYWUxNzA0ZDRlZCIsICJyb2xlIjogInt0b3BpY30g
|
||||
UmVwb3J0aW5nIEFuYWx5c3RcbiIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4X2l0ZXIiOiAyMCwg
|
||||
Im1heF9ycG0iOiBudWxsLCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwgImxsbSI6ICJncHQt
|
||||
NG8iLCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNlLCAiYWxsb3dfY29kZV9leGVjdXRpb24/
|
||||
IjogZmFsc2UsICJtYXhfcmV0cnlfbGltaXQiOiAyLCAidG9vbHNfbmFtZXMiOiBbXX1dSpMECgpj
|
||||
cmV3X3Rhc2tzEoQECoEEW3sia2V5IjogIjZhZmM0YjM5NjI1OWZiYjc2ODFmNTZjNzc1NWNjOTM3
|
||||
IiwgImlkIjogImIxZjQ5ODJiLTRjZGItNDk1MC04ZmNjLWMwZDcxNzRhYzY0NiIsICJhc3luY19l
|
||||
eGVjdXRpb24/IjogZmFsc2UsICJodW1hbl9pbnB1dD8iOiBmYWxzZSwgImFnZW50X3JvbGUiOiAi
|
||||
e3RvcGljfSBTZW5pb3IgRGF0YSBSZXNlYXJjaGVyXG4iLCAiYWdlbnRfa2V5IjogIjczYzM0OWM5
|
||||
M2MxNjNiNWQ0ZGY5OGE2NGZhYzFjNDMwIiwgInRvb2xzX25hbWVzIjogW119LCB7ImtleSI6ICJi
|
||||
MTdiMTg4ZGJmMTRmOTNhOThlNWI5NWFhZDM2NzU3NyIsICJpZCI6ICIyY2VkNGVhNC01YjcwLTRh
|
||||
MDctOTEyOS00MzQ2ZDQ1OWM4NjIiLCAiYXN5bmNfZXhlY3V0aW9uPyI6IGZhbHNlLCAiaHVtYW5f
|
||||
aW5wdXQ/IjogZmFsc2UsICJhZ2VudF9yb2xlIjogInt0b3BpY30gUmVwb3J0aW5nIEFuYWx5c3Rc
|
||||
biIsICJhZ2VudF9rZXkiOiAiYmIwNjgzNzdjMTY0MWJlNmQ3ZDk3YTUxNjU5ZGI2MTMiLCAidG9v
|
||||
bHNfbmFtZXMiOiBbXX1degIYAYUBAAEAABKOAgoQOaRyuH2UERJ3sHC1ImhOgxIIq8DZc4P2KYMq
|
||||
DFRhc2sgQ3JlYXRlZDABOTA127narwgYQVjV27narwgYSi4KCGNyZXdfa2V5EiIKIGYzNDZhOWFk
|
||||
NmQ3MzA2M2UwNjc3YjE3Y2U5YzUwMTc3SjEKB2NyZXdfaWQSJgokNmM4OTQ3MzctMzViYy00YWQx
|
||||
LWIxNjYtYWU2Nzg4YTEwOGFmSi4KCHRhc2tfa2V5EiIKIDZhZmM0YjM5NjI1OWZiYjc2ODFmNTZj
|
||||
Nzc1NWNjOTM3SjEKB3Rhc2tfaWQSJgokYjFmNDk4MmItNGNkYi00OTUwLThmY2MtYzBkNzE3NGFj
|
||||
NjQ2egIYAYUBAAEAAA==
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- '1894'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
User-Agent:
|
||||
- OTel-OTLP-Exporter-Python/1.27.0
|
||||
method: POST
|
||||
uri: https://telemetry.crewai.com:4319/v1/traces
|
||||
response:
|
||||
body:
|
||||
string: "\n\0"
|
||||
headers:
|
||||
Content-Length:
|
||||
- '2'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
Date:
|
||||
- Sun, 17 Nov 2024 07:10:11 GMT
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: '{"messages": [{"role": "system", "content": "You are {topic} Senior Data
|
||||
Researcher\n. You''re a seasoned researcher with a knack for uncovering the
|
||||
latest developments in {topic}. Known for your ability to find the most relevant
|
||||
information and present it in a clear and concise manner.\n\nYour personal goal
|
||||
is: Uncover cutting-edge developments in {topic}\n\nTo give my best complete
|
||||
final answer to the task use the exact following format:\n\nThought: I now can
|
||||
give a great answer\nFinal Answer: Your final answer must be the great and the
|
||||
most complete as possible, it must be outcome described.\n\nI MUST use these
|
||||
formats, my job depends on it!"}, {"role": "user", "content": "\nCurrent Task:
|
||||
Conduct a thorough research about {topic} Make sure you find any interesting
|
||||
and relevant information given the current year is 2024.\n\n\nThis is the expect
|
||||
criteria for your final answer: A list with 10 bullet points of the most relevant
|
||||
information about {topic}\n\nyou MUST return the actual complete content as
|
||||
the final answer, not a summary.\n\nBegin! This is VERY important to you, use
|
||||
the tools available and give your best Final Answer, your job depends on it!\n\nThought:"}],
|
||||
"model": "gpt-4o", "stop": ["\nObservation:"], "stream": false}'
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1250'
|
||||
content-type:
|
||||
- application/json
|
||||
cookie:
|
||||
- __cf_bm=08pKRcLhS1PDw0mYfL2jz19ac6M.T31GoiMuI5DlX6w-1731827382-1.0.1.1-UfOLu3AaIUuXP1sGzdV6oggJ1q7iMTC46t08FDhYVrKcW5YmD4CbifudOJiSgx8h0JLTwZdgk.aG05S0eAO_PQ;
|
||||
_cfuvid=74kaPOoAcp8YRSA0XocQ1FFNksu9V0_KiWdQfo7wQuQ-1731827382509-0.0.1.1-604800000
|
||||
host:
|
||||
- api.openai.com
|
||||
user-agent:
|
||||
- OpenAI/Python 1.52.1
|
||||
x-stainless-arch:
|
||||
- arm64
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- MacOS
|
||||
x-stainless-package-version:
|
||||
- 1.52.1
|
||||
x-stainless-raw-response:
|
||||
- 'true'
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.11.7
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
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
|
||||
headers:
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 8e3de645a8666217-GRU
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Sun, 17 Nov 2024 07:10:16 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
access-control-expose-headers:
|
||||
- X-Request-ID
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
openai-organization:
|
||||
- crewai-iuxna1
|
||||
openai-processing-ms:
|
||||
- '5537'
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
strict-transport-security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
x-ratelimit-limit-requests:
|
||||
- '10000'
|
||||
x-ratelimit-limit-tokens:
|
||||
- '30000000'
|
||||
x-ratelimit-remaining-requests:
|
||||
- '9999'
|
||||
x-ratelimit-remaining-tokens:
|
||||
- '29999711'
|
||||
x-ratelimit-reset-requests:
|
||||
- 6ms
|
||||
x-ratelimit-reset-tokens:
|
||||
- 0s
|
||||
x-request-id:
|
||||
- req_220e7945d04e84ab7b58c252c98630b5
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: !!binary |
|
||||
Cs4CCiQKIgoMc2VydmljZS5uYW1lEhIKEGNyZXdBSS10ZWxlbWV0cnkSpQIKEgoQY3Jld2FpLnRl
|
||||
bGVtZXRyeRKOAgoQrqG+rs9H9Iyyqr2ZU1qS4RIIWspPh5zdoVMqDFRhc2sgQ3JlYXRlZDABOeiB
|
||||
tw7crwgYQZgvuQ7crwgYSi4KCGNyZXdfa2V5EiIKIGYzNDZhOWFkNmQ3MzA2M2UwNjc3YjE3Y2U5
|
||||
YzUwMTc3SjEKB2NyZXdfaWQSJgokNmM4OTQ3MzctMzViYy00YWQxLWIxNjYtYWU2Nzg4YTEwOGFm
|
||||
Si4KCHRhc2tfa2V5EiIKIGIxN2IxODhkYmYxNGY5M2E5OGU1Yjk1YWFkMzY3NTc3SjEKB3Rhc2tf
|
||||
aWQSJgokMmNlZDRlYTQtNWI3MC00YTA3LTkxMjktNDM0NmQ0NTljODYyegIYAYUBAAEAAA==
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- '337'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
User-Agent:
|
||||
- OTel-OTLP-Exporter-Python/1.27.0
|
||||
method: POST
|
||||
uri: https://telemetry.crewai.com:4319/v1/traces
|
||||
response:
|
||||
body:
|
||||
string: "\n\0"
|
||||
headers:
|
||||
Content-Length:
|
||||
- '2'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
Date:
|
||||
- Sun, 17 Nov 2024 07:10:22 GMT
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: '{"messages": [{"role": "system", "content": "You are {topic} Reporting
|
||||
Analyst\n. You''re a meticulous analyst with a keen eye for detail. You''re
|
||||
known for your ability to turn complex data into clear and concise reports,
|
||||
making it easy for others to understand and act on the information you provide.\nYour
|
||||
personal goal is: Create detailed reports based on {topic} data analysis and
|
||||
research findings\n\nTo give my best complete final answer to the task use the
|
||||
exact following format:\n\nThought: I now can give a great answer\nFinal Answer:
|
||||
Your final answer must be the great and the most complete as possible, it must
|
||||
be outcome described.\n\nI MUST use these formats, my job depends on it!"},
|
||||
{"role": "user", "content": "\nCurrent Task: Review the context you got and
|
||||
expand each topic into a full section for a report. Make sure the report is
|
||||
detailed and contains any and all relevant information.\n\n\nThis is the expect
|
||||
criteria for your final answer: A fully fledge reports with the mains topics,
|
||||
each with a full section of information. Formatted as markdown without ''```''\n\nyou
|
||||
MUST return the actual complete content as the final answer, not a summary.\n\nThis
|
||||
is the context you''re working with:\n1. **Breakthrough in Quantum Computing**:
|
||||
By 2024, advancements in quantum computing have led to more reliable qubit processing,
|
||||
paving the way for practical applications in cryptography, complex system simulations,
|
||||
and optimization problems.\n\n2. **AI Integration in Healthcare**: Artificial
|
||||
intelligence continues to transform healthcare, with AI algorithms now more
|
||||
accurately diagnosing diseases, predicting patient outcomes, and personalizing
|
||||
treatment plans than ever before.\n\n3. **Renewable Energy Innovations**: The
|
||||
year 2024 has seen significant improvements in renewable energy technologies.
|
||||
Next-generation solar panels and wind turbines are more efficient, leading to
|
||||
widespread adoption and reduced reliance on fossil fuels.\n\n4. **5G Expansion
|
||||
and 6G Development**: The global rollout of 5G technology reaches near completion,
|
||||
and research into 6G has commenced. This development promises to introduce unprecedented
|
||||
data transfer speeds and connectivity.\n\n5. **Advances in Biotechnology**:
|
||||
Gene-editing technologies, such as CRISPR, have advanced to a stage where genetic
|
||||
disorders can be effectively treated, sparking ethical debates and regulatory
|
||||
considerations.\n\n6. **Space Exploration Milestones**: The space industry achieves
|
||||
new milestones with the establishment of permanent lunar bases and the first
|
||||
manned missions to Mars, driven by both government and private sector collaboration.\n\n7.
|
||||
**Sustainable Agriculture Practices**: In response to climate change challenges,
|
||||
sustainable agriculture practices, including vertical farming and precision
|
||||
agriculture, are gaining traction globally, boosting food production and reducing
|
||||
environmental impact.\n\n8. **Cybersecurity Enhancements**: With increasing
|
||||
digital threats, 2024 sees robust advancements in cybersecurity technologies,
|
||||
including AI-driven threat detection, and enhanced data encryption methodologies.\n\n9.
|
||||
**Transportation Innovation**: Public transportation and electric vehicle technology
|
||||
continue to evolve with the introduction of autonomous public transit systems
|
||||
and improvements in EV battery longevity and charging infrastructures.\n\n10.
|
||||
**Mental Health Awareness**: A global increase in mental health awareness leads
|
||||
to increased funding for research and the integration of digital therapies and
|
||||
apps that provide more accessible mental health support.\n\nThese bullet points
|
||||
summarize significant areas of development and interest in the given topic in
|
||||
2024, highlighting the profound impacts in various fields.\n\nBegin! This is
|
||||
VERY important to you, use the tools available and give your best Final Answer,
|
||||
your job depends on it!\n\nThought:"}], "model": "gpt-4o", "stop": ["\nObservation:"],
|
||||
"stream": false}'
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '3935'
|
||||
content-type:
|
||||
- application/json
|
||||
cookie:
|
||||
- __cf_bm=08pKRcLhS1PDw0mYfL2jz19ac6M.T31GoiMuI5DlX6w-1731827382-1.0.1.1-UfOLu3AaIUuXP1sGzdV6oggJ1q7iMTC46t08FDhYVrKcW5YmD4CbifudOJiSgx8h0JLTwZdgk.aG05S0eAO_PQ;
|
||||
_cfuvid=74kaPOoAcp8YRSA0XocQ1FFNksu9V0_KiWdQfo7wQuQ-1731827382509-0.0.1.1-604800000
|
||||
host:
|
||||
- api.openai.com
|
||||
user-agent:
|
||||
- OpenAI/Python 1.52.1
|
||||
x-stainless-arch:
|
||||
- arm64
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- MacOS
|
||||
x-stainless-package-version:
|
||||
- 1.52.1
|
||||
x-stainless-raw-response:
|
||||
- 'true'
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.11.7
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
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
|
||||
headers:
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 8e3de6697f976217-GRU
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Sun, 17 Nov 2024 07:10:27 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
access-control-expose-headers:
|
||||
- X-Request-ID
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
openai-organization:
|
||||
- crewai-iuxna1
|
||||
openai-processing-ms:
|
||||
- '10658'
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
strict-transport-security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
x-ratelimit-limit-requests:
|
||||
- '10000'
|
||||
x-ratelimit-limit-tokens:
|
||||
- '30000000'
|
||||
x-ratelimit-remaining-requests:
|
||||
- '9999'
|
||||
x-ratelimit-remaining-tokens:
|
||||
- '29999045'
|
||||
x-ratelimit-reset-requests:
|
||||
- 6ms
|
||||
x-ratelimit-reset-tokens:
|
||||
- 1ms
|
||||
x-request-id:
|
||||
- req_f0af67637da5bc0e6b11fc3e5db59f62
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user