mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-10-19 06:25:09 +00:00
Compare commits
1 Commits
1.14.7a4
...
fix/interp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f5928e4bb |
@@ -4,26 +4,6 @@ description: "تحديثات المنتج والتحسينات وإصلاحات
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="9 يونيو 2026">
|
||||
## v1.14.7a4
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.7a4)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الميزات
|
||||
- نقل وقت التشغيل @listen/@router لقراءة من FlowDefinition
|
||||
- إضافة واجهات خلفية افتراضية قابلة للتوصيل للذاكرة، والمعرفة، وrag، وflow
|
||||
|
||||
### الوثائق
|
||||
- تحديث سجل التغييرات والإصدار لـ v1.14.7a3
|
||||
|
||||
## المساهمون
|
||||
|
||||
@greysonlalonde, @mattatcha, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="8 يونيو 2026">
|
||||
## v1.14.7a3
|
||||
|
||||
|
||||
@@ -4,26 +4,6 @@ description: "Product updates, improvements, and bug fixes for CrewAI"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="Jun 09, 2026">
|
||||
## v1.14.7a4
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.7a4)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Migrate @listen/@router runtime to read from FlowDefinition
|
||||
- Add pluggable default backends for memory, knowledge, rag, and flow
|
||||
|
||||
### Documentation
|
||||
- Update changelog and version for v1.14.7a3
|
||||
|
||||
## Contributors
|
||||
|
||||
@greysonlalonde, @mattatcha, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Jun 08, 2026">
|
||||
## v1.14.7a3
|
||||
|
||||
|
||||
@@ -4,26 +4,6 @@ description: "CrewAI의 제품 업데이트, 개선 사항 및 버그 수정"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="2026년 6월 9일">
|
||||
## v1.14.7a4
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.7a4)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- @listen/@router 런타임을 FlowDefinition에서 읽도록 마이그레이션
|
||||
- 메모리, 지식, rag 및 flow에 대한 플러그형 기본 백엔드 추가
|
||||
|
||||
### 문서
|
||||
- v1.14.7a3에 대한 변경 로그 및 버전 업데이트
|
||||
|
||||
## 기여자
|
||||
|
||||
@greysonlalonde, @mattatcha, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 6월 8일">
|
||||
## v1.14.7a3
|
||||
|
||||
|
||||
@@ -4,26 +4,6 @@ description: "Atualizações de produto, melhorias e correções do CrewAI"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="09 jun 2026">
|
||||
## v1.14.7a4
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.7a4)
|
||||
|
||||
## O Que Mudou
|
||||
|
||||
### Funcionalidades
|
||||
- Migrar a execução @listen/@router para ler a partir de FlowDefinition
|
||||
- Adicionar backends padrão plugáveis para memória, conhecimento, rag e flow
|
||||
|
||||
### Documentação
|
||||
- Atualizar changelog e versão para v1.14.7a3
|
||||
|
||||
## Contributors
|
||||
|
||||
@greysonlalonde, @mattatcha, @vinibrsl
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="08 jun 2026">
|
||||
## v1.14.7a3
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ authors = [
|
||||
]
|
||||
requires-python = ">=3.10, <3.14"
|
||||
dependencies = [
|
||||
"crewai-core==1.14.7a4",
|
||||
"crewai-core==1.14.7a3",
|
||||
"click>=8.1.7,<9",
|
||||
"pydantic>=2.11.9,<2.13",
|
||||
"pydantic-settings~=2.10.1",
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "1.14.7a4"
|
||||
__version__ = "1.14.7a3"
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
|
||||
authors = [{ name = "Your Name", email = "you@example.com" }]
|
||||
requires-python = ">=3.10,<3.14"
|
||||
dependencies = [
|
||||
"crewai[tools]==1.14.7a4"
|
||||
"crewai[tools]==1.14.7a3"
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
|
||||
authors = [{ name = "Your Name", email = "you@example.com" }]
|
||||
requires-python = ">=3.10,<3.14"
|
||||
dependencies = [
|
||||
"crewai[tools]==1.14.7a4"
|
||||
"crewai[tools]==1.14.7a3"
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "Power up your crews with {{folder_name}}"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10,<3.14"
|
||||
dependencies = [
|
||||
"crewai[tools]==1.14.7a4"
|
||||
"crewai[tools]==1.14.7a3"
|
||||
]
|
||||
|
||||
[tool.crewai]
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "1.14.7a4"
|
||||
__version__ = "1.14.7a3"
|
||||
|
||||
@@ -152,4 +152,4 @@ __all__ = [
|
||||
"wrap_file_source",
|
||||
]
|
||||
|
||||
__version__ = "1.14.7a4"
|
||||
__version__ = "1.14.7a3"
|
||||
|
||||
@@ -10,7 +10,7 @@ requires-python = ">=3.10, <3.14"
|
||||
dependencies = [
|
||||
"pytube~=15.0.0",
|
||||
"requests>=2.33.0,<3",
|
||||
"crewai==1.14.7a4",
|
||||
"crewai==1.14.7a3",
|
||||
"tiktoken>=0.8.0,<0.13",
|
||||
"beautifulsoup4~=4.13.4",
|
||||
"python-docx~=1.2.0",
|
||||
|
||||
@@ -330,4 +330,4 @@ __all__ = [
|
||||
"ZapierActionTools",
|
||||
]
|
||||
|
||||
__version__ = "1.14.7a4"
|
||||
__version__ = "1.14.7a3"
|
||||
|
||||
@@ -8,8 +8,8 @@ authors = [
|
||||
]
|
||||
requires-python = ">=3.10, <3.14"
|
||||
dependencies = [
|
||||
"crewai-core==1.14.7a4",
|
||||
"crewai-cli==1.14.7a4",
|
||||
"crewai-core==1.14.7a3",
|
||||
"crewai-cli==1.14.7a3",
|
||||
# Core Dependencies
|
||||
"pydantic>=2.11.9,<2.13",
|
||||
"openai>=2.30.0,<3",
|
||||
@@ -54,7 +54,7 @@ Repository = "https://github.com/crewAIInc/crewAI"
|
||||
|
||||
[project.optional-dependencies]
|
||||
tools = [
|
||||
"crewai-tools==1.14.7a4",
|
||||
"crewai-tools==1.14.7a3",
|
||||
]
|
||||
embeddings = [
|
||||
"tiktoken>=0.8.0,<0.13"
|
||||
|
||||
@@ -48,7 +48,7 @@ def _suppress_pydantic_deprecation_warnings() -> None:
|
||||
|
||||
_suppress_pydantic_deprecation_warnings()
|
||||
|
||||
__version__ = "1.14.7a4"
|
||||
__version__ = "1.14.7a3"
|
||||
|
||||
_LAZY_IMPORTS: dict[str, tuple[str, str]] = {
|
||||
"Memory": ("crewai.memory.unified_memory", "Memory"),
|
||||
|
||||
@@ -7,7 +7,6 @@ from copy import copy as shallow_copy
|
||||
from hashlib import md5
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Annotated,
|
||||
@@ -142,7 +141,10 @@ from crewai.utilities.streaming import (
|
||||
signal_end,
|
||||
signal_error,
|
||||
)
|
||||
from crewai.utilities.string_utils import sanitize_tool_name
|
||||
from crewai.utilities.string_utils import (
|
||||
extract_template_variables,
|
||||
sanitize_tool_name,
|
||||
)
|
||||
from crewai.utilities.task_output_storage_handler import TaskOutputStorageHandler
|
||||
from crewai.utilities.training_handler import CrewTrainingHandler
|
||||
|
||||
@@ -1960,20 +1962,24 @@ class Crew(FlowTrackable, BaseModel):
|
||||
Scans each task's 'description' + 'expected_output', and each agent's
|
||||
'role', 'goal', and 'backstory'.
|
||||
|
||||
Only placeholders that interpolation can actually fill are returned;
|
||||
non-identifier expressions such as ``{x if x else "y"}`` are ignored so
|
||||
they are not surfaced as required inputs (matching interpolation
|
||||
behavior, see :func:`extract_template_variables`).
|
||||
|
||||
Returns a set of all discovered placeholder names.
|
||||
"""
|
||||
placeholder_pattern = re.compile(r"\{(.+?)}")
|
||||
required_inputs: set[str] = set()
|
||||
|
||||
for task in self.tasks:
|
||||
# description and expected_output might contain e.g. {topic}, {user_name}
|
||||
text = f"{task.description or ''} {task.expected_output or ''}"
|
||||
required_inputs.update(placeholder_pattern.findall(text))
|
||||
required_inputs.update(extract_template_variables(text))
|
||||
|
||||
for agent in self.agents:
|
||||
# role, goal, backstory might have placeholders like {role_detail}, etc.
|
||||
text = f"{agent.role or ''} {agent.goal or ''} {agent.backstory or ''}"
|
||||
required_inputs.update(placeholder_pattern.findall(text))
|
||||
required_inputs.update(extract_template_variables(text))
|
||||
|
||||
return required_inputs
|
||||
|
||||
|
||||
@@ -23,6 +23,26 @@ def _duplicate_separator_pattern(separator: str) -> re.Pattern[str]:
|
||||
return re.compile(f"(?:{re.escape(separator)}){{2,}}")
|
||||
|
||||
|
||||
def extract_template_variables(input_string: str | None) -> list[str]:
|
||||
"""Return the template variable names referenced in a string.
|
||||
|
||||
Only recognizes placeholders that interpolation can actually fill, i.e.
|
||||
``{name}`` where ``name`` starts with a letter/underscore and contains only
|
||||
letters, numbers, underscores, and hyphens. Expressions such as
|
||||
``{x if x else "y"}`` or JSON snippets are intentionally ignored so they are
|
||||
never treated as required inputs.
|
||||
|
||||
Args:
|
||||
input_string: The string to scan. May be ``None`` or empty.
|
||||
|
||||
Returns:
|
||||
The matched variable names, in order of appearance (with duplicates).
|
||||
"""
|
||||
if not input_string:
|
||||
return []
|
||||
return _VARIABLE_PATTERN.findall(input_string)
|
||||
|
||||
|
||||
def sanitize_tool_name(name: str, max_length: int = _MAX_TOOL_NAME_LENGTH) -> str:
|
||||
"""Sanitize tool name for LLM provider compatibility.
|
||||
|
||||
|
||||
@@ -3895,6 +3895,29 @@ def test_fetch_inputs():
|
||||
)
|
||||
|
||||
|
||||
def test_fetch_inputs_ignores_non_identifier_placeholders():
|
||||
agent = Agent(
|
||||
role="Report writer",
|
||||
goal="Write a report for {company_name}.",
|
||||
backstory="Expert reporter.",
|
||||
)
|
||||
|
||||
task = Task(
|
||||
description=(
|
||||
'Greet {company_name if company_name else "Individual Client"} '
|
||||
"and summarize {search_period}."
|
||||
),
|
||||
expected_output="A summary for {company_name}.",
|
||||
agent=agent,
|
||||
)
|
||||
|
||||
crew = Crew(agents=[agent], tasks=[task])
|
||||
|
||||
# Only the simple {company_name} placeholders are returned; the inline conditional
|
||||
# expression (which interpolation cannot fill) is ignored.
|
||||
assert crew.fetch_inputs() == {"company_name", "search_period"}
|
||||
|
||||
|
||||
@pytest.mark.vcr()
|
||||
def test_task_tools_preserve_code_execution_tools():
|
||||
"""
|
||||
|
||||
@@ -1,7 +1,45 @@
|
||||
from typing import Any, Dict, List, Union
|
||||
|
||||
import pytest
|
||||
from crewai.utilities.string_utils import interpolate_only
|
||||
from crewai.utilities.string_utils import (
|
||||
extract_template_variables,
|
||||
interpolate_only,
|
||||
)
|
||||
|
||||
|
||||
class TestExtractTemplateVariables:
|
||||
"""Tests for extract_template_variables in string_utils.py."""
|
||||
|
||||
def test_extracts_simple_identifiers(self):
|
||||
assert extract_template_variables("Hi {name}, see {topic}.") == [
|
||||
"name",
|
||||
"topic",
|
||||
]
|
||||
|
||||
def test_allows_underscores_and_hyphens(self):
|
||||
assert extract_template_variables("{user_name} {role-detail}") == [
|
||||
"user_name",
|
||||
"role-detail",
|
||||
]
|
||||
|
||||
def test_ignores_inline_expressions(self):
|
||||
text = '{company_name if company_name else "Individual Client"}'
|
||||
assert extract_template_variables(text) == []
|
||||
|
||||
def test_ignores_json_like_braces(self):
|
||||
assert extract_template_variables('{"key": "value"}') == []
|
||||
|
||||
def test_matches_what_interpolation_fills(self):
|
||||
text = 'Use {topic} and {x if x else "y"}.'
|
||||
variables = extract_template_variables(text)
|
||||
assert variables == ["topic"]
|
||||
# interpolation fills exactly the extracted variable and leaves the rest
|
||||
result = interpolate_only(text, {"topic": "AI"})
|
||||
assert result == 'Use AI and {x if x else "y"}.'
|
||||
|
||||
@pytest.mark.parametrize("value", [None, ""])
|
||||
def test_handles_empty_input(self, value):
|
||||
assert extract_template_variables(value) == []
|
||||
|
||||
|
||||
class TestInterpolateOnly:
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""CrewAI development tools."""
|
||||
|
||||
__version__ = "1.14.7a4"
|
||||
__version__ = "1.14.7a3"
|
||||
|
||||
Reference in New Issue
Block a user