Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ lint:
poetry run ruff format --preview src/ tests/
poetry run black --preview --enable-unstable-feature=string_processing src/ tests/
poetry run ruff check --fix src/ tests/
poetry run mypy src/avalan

test:
poetry sync --extras test
Expand Down
199 changes: 183 additions & 16 deletions poetry.lock

Large diffs are not rendered by default.

68 changes: 68 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,74 @@ version = "1.3.10"
[tool.poetry.group.dev.dependencies]
ruff = "^0.11.11"
black = "^25.1.0"
mypy = "^1.14.0"

[tool.mypy]
python_version = "3.11"
files = ["src/avalan"]
plugins = ["pydantic.mypy"]
strict = false
warn_return_any = true
warn_unused_ignores = false
warn_redundant_casts = true
warn_unused_configs = true
show_error_codes = true
show_column_numbers = true
pretty = true
check_untyped_defs = true
disallow_untyped_defs = false
disallow_incomplete_defs = false
disallow_untyped_decorators = false

[[tool.mypy.overrides]]
module = [
"a2a.*",
"anthropic.*",
"bitsandbytes.*",
"boto3.*",
"botocore.*",
"diffusers.*",
"elasticsearch.*",
"faiss.*",
"google.*",
"huggingface_hub.*",
"imageio.*",
"jinja2.*",
"keyring.*",
"litellm.*",
"markitdown.*",
"markdownify.*",
"mcp.*",
"mlx.*",
"mlx_lm.*",
"numpy.*",
"openai.*",
"pandas.*",
"pgvector.*",
"PIL.*",
"playwright.*",
"psycopg.*",
"psycopg_pool.*",
"pydantic.*",
"RestrictedPython.*",
"rich.*",
"soundfile.*",
"sqlalchemy.*",
"sqlglot.*",
"sympy.*",
"tiktoken.*",
"torch.*",
"torchaudio.*",
"torchvision.*",
"tqdm.*",
"transformers.*",
"tree_sitter.*",
"tree_sitter_python.*",
"uvicorn.*",
"vllm.*",
"youtube_transcript_api.*",
]
ignore_missing_imports = true

[tool.poetry-dynamic-versioning]
enable = true
8 changes: 4 additions & 4 deletions src/avalan/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
from packaging.version import Version, parse


def _config() -> dict:
def _config() -> dict[str, str]:
config = metadata("avalan")
package_version = metadata_version("avalan")
return {
"name": config["Name"],
"name": str(config["Name"]),
"version": package_version,
"license": config["License"],
"license": str(config["License"]),
"url": "https://avalan.ai",
}


config = _config()
config: dict[str, str] = _config()


def license() -> str:
Expand Down
18 changes: 14 additions & 4 deletions src/avalan/agent/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ async def input_token_count(self) -> int | None:
},
)
)
count = self._model.input_token_count(
assert hasattr(self._model, "input_token_count")
count: int | None = self._model.input_token_count(
self._last_prompt[0],
system_prompt=self._last_prompt[1],
developer_prompt=self._last_prompt[2],
Expand Down Expand Up @@ -158,6 +159,7 @@ async def __call__(
},
)
)
assert context.input is not None
output = await self._run(context, context.input, **run_args)
await self._event_manager.trigger(
Event(
Expand Down Expand Up @@ -246,11 +248,18 @@ async def _run(
if previous_message:
await self.sync_message(previous_message)

for current_message in input_value:
for current_item in input_value:
current_message = (
current_item
if isinstance(current_item, Message)
else Message(role=MessageRole.USER, content=current_item)
)
await self.sync_message(current_message)

# Make recent memory the new model input
input_value = [rm.message for rm in self._memory.recent_messages]
recent = self._memory.recent_messages
assert recent is not None
input_value = [rm.message for rm in recent]

# Have model generate output from input

Expand Down Expand Up @@ -289,7 +298,7 @@ async def _run(
tool=self._tool,
context=context,
)
output = await self._model_manager(model_task)
output: TextGenerationResponse = await self._model_manager(model_task)
await self._event_manager.trigger(
Event(
type=EventType.MODEL_EXECUTE_AFTER,
Expand Down Expand Up @@ -344,6 +353,7 @@ async def sync_message(self, message: Message) -> None:
},
)
)
assert self._model.model_id is not None
await self._memory.append_message(
EngineMessage(
agent_id=self._id,
Expand Down
13 changes: 11 additions & 2 deletions src/avalan/agent/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class OrchestratorLoader:
)
_OPENAI_RESPONSES_ALIASES = frozenset({"response", "responses"})

_event_manager: EventManager
_hub: HuggingfaceHub
_logger: Logger
_participant_id: UUID
Expand All @@ -75,12 +76,19 @@ def __init__(
logger: Logger,
participant_id: UUID,
stack: AsyncExitStack,
event_manager: EventManager | None = None,
) -> None:
self._event_manager = event_manager or EventManager()
self._hub = hub
self._logger = logger
self._participant_id = participant_id
self._stack = stack

@property
def event_manager(self) -> EventManager:
"""Return the event manager instance."""
return self._event_manager

@staticmethod
def parse_permanent_store_value(
value: str,
Expand Down Expand Up @@ -372,7 +380,7 @@ async def from_file(
agent_config=agent_config,
uri=uri,
engine_config=engine_config,
tools=enable_tools,
tools=enable_tools or [],
call_options=call_options,
template_vars=template_vars,
memory_permanent_message=memory_permanent_message,
Expand Down Expand Up @@ -495,7 +503,7 @@ async def from_settings(

_l("Loading event manager")

event_manager = EventManager()
event_manager = self._event_manager
if settings.log_events:

def _log_event(event: Event) -> None:
Expand Down Expand Up @@ -607,6 +615,7 @@ def _log_event(event: Event) -> None:

assert settings.agent_id

agent: Orchestrator
if settings.orchestrator_type == "json":
assert settings.json_config is not None
agent = self._load_json_orchestrator(
Expand Down
39 changes: 28 additions & 11 deletions src/avalan/agent/orchestrator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
Message,
MessageContentText,
MessageRole,
TransformerEngineSettings,
)
from ...entities import Modality as Modality
from ...event import Event, EventType
Expand All @@ -14,18 +15,24 @@
from ...model.call import ModelCallContext
from ...model.engine import Engine
from ...model.manager import ModelManager
from ...model.response.text import (
TextGenerationResponse as TextGenerationResponse,
)
from ...tool.manager import ToolManager
from .. import (
AgentOperation,
EngineEnvironment,
InputType,
NoOperationAvailableException,
Specification,
)
from .. import (
EngineEnvironment as EngineEnvironment,
)
from ..engine import EngineAgent
from ..renderer import Renderer, TemplateEngineAgent
from .response.orchestrator_response import OrchestratorResponse

from collections.abc import Callable, Coroutine
from contextlib import ExitStack
from dataclasses import asdict
from json import dumps
Expand All @@ -46,7 +53,7 @@ class Orchestrator:
_memory: MemoryManager
_tool: ToolManager
_event_manager: EventManager
_engine_agents: dict[EngineEnvironment, EngineAgent] = {}
_engine_agents: dict[str, EngineAgent] = {}
_engines_stack: ExitStack = ExitStack()
_operation_step: int | None = None
_model_ids: set[str] = set()
Expand Down Expand Up @@ -108,7 +115,9 @@ def id(self) -> UUID:
return self._id

@property
def input_token_count(self) -> int | None:
def input_token_count(
self,
) -> Callable[[], Coroutine[Any, Any, int | None]] | None:
return (
self._last_engine_agent.input_token_count
if self._last_engine_agent
Expand Down Expand Up @@ -243,7 +252,7 @@ async def __call__(self, input: Input, **kwargs) -> OrchestratorResponse:

return OrchestratorResponse(
messages,
result,
result, # type: ignore[arg-type]
engine_agent,
operation,
engine_args,
Expand All @@ -264,7 +273,11 @@ async def __aenter__(self):
environment = operation.environment
environment_hash = dumps(asdict(environment))
if environment_hash not in self._engine_agents:
assert environment.engine_uri.model_id is not None
model_ids.append(environment.engine_uri.model_id)
assert isinstance(
environment.settings, TransformerEngineSettings
)
engine = self._model_manager.load_engine(
environment.engine_uri,
environment.settings,
Expand Down Expand Up @@ -359,18 +372,22 @@ def _input_messages(
else message.content
)
render_vars.update({"input": message_content})
content = (
self._renderer(self._user_template, **render_vars)
if self._user_template
else self._renderer.from_string(
if self._user_template:
content = self._renderer(
self._user_template, **render_vars
)
else:
assert self._user is not None
content = self._renderer.from_string(
self._user, template_vars=render_vars
)
)
message = Message(role=message.role, content=content)

if isinstance(input, list):
input[-1] = message
input[-1] = message # type: ignore[call-overload]
else:
input = message

return input
# The return type must be Message | list[Message] per the signature
assert isinstance(input, (Message, list))
return input # type: ignore[return-value]
13 changes: 10 additions & 3 deletions src/avalan/agent/orchestrator/orchestrators/default.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
from ....agent import AgentOperation, EngineEnvironment, Goal, Specification
from ....agent import (
AgentOperation,
EngineEnvironment,
Goal,
Role,
Specification,
)
from ....agent.orchestrator import Orchestrator
from ....entities import EngineUri, Modality, TransformerEngineSettings
from ....event.manager import EventManager
Expand Down Expand Up @@ -47,7 +53,7 @@ def __init__(
)
else:
specification = Specification(
role=role,
role=Role(persona=[role]) if role else None,
goal=(
Goal(task=task, instructions=[instructions])
if task and instructions
Expand All @@ -66,7 +72,8 @@ def __init__(
AgentOperation(
specification=specification,
environment=EngineEnvironment(
engine_uri=engine_uri, settings=settings
engine_uri=engine_uri,
settings=settings or TransformerEngineSettings(),
),
modality=Modality.TEXT_GENERATION,
),
Expand Down
Loading
Loading