diff --git a/.gitignore b/.gitignore
index f354bfc..c0918cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -142,3 +142,4 @@ test_outputs/
agentfly/data/
*.ipynb
+./*.jpg
\ No newline at end of file
diff --git a/README.md b/README.md
index 4d50bd0..7e73d95 100644
--- a/README.md
+++ b/README.md
@@ -54,11 +54,11 @@ Please refer to [installation.md](docs/start/installation.md) for custmoized ins
```python
# Really small example to build an agent and run
from agentfly.agents import HFAgent
-from agentfly.tools import calculate
+from agentfly.tools import calculator
messages = [{"role": "user", "content": "What is the result of 1 + 1?"}]
agent = HFAgent(
model_name_or_path="Qwen/Qwen2.5-3B-Instruct",
- tools=[calculate],
+ tools=[calculator],
template="qwen2.5",
backend="async_vllm",
)
diff --git a/agentfly/agents/__init__.py b/agentfly/agents/__init__.py
index b3d2564..ec8166e 100644
--- a/agentfly/agents/__init__.py
+++ b/agentfly/agents/__init__.py
@@ -2,5 +2,4 @@
from .specialized.code_agent import CodeAgent
from .specialized.think_agent import ThinkAgent
from .specialized.gui_agent import GUIAgent
-from .specialized.hf_agent import HFAgent
-from .templates.utils import process_vision_info, tokenize_conversation, tokenize_conversations
\ No newline at end of file
+from .specialized.hf_agent import HFAgent
\ No newline at end of file
diff --git a/agentfly/agents/agent_base.py b/agentfly/agents/agent_base.py
index 4284e4d..c2685bc 100644
--- a/agentfly/agents/agent_base.py
+++ b/agentfly/agents/agent_base.py
@@ -2,7 +2,7 @@
from collections import defaultdict
import json
from .utils.messages import MessagesList
-from .templates.templates import get_template
+from ..templates.templates import get_template
from ..__init__ import AGENT_DATA_DIR
from .llm_backends import (
AsyncVLLMBackend,
@@ -15,8 +15,7 @@
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
import numpy as np
import torch
-from .templates.utils import tokenize_conversations
-from .templates.vision_processor import is_vision_template
+from ..templates import tokenize_conversations
from .chain.chain_base import ChainRollout
import os
import transformers
diff --git a/agentfly/agents/llm_backends/llm_backends.py b/agentfly/agents/llm_backends/llm_backends.py
index 340e985..72331d2 100644
--- a/agentfly/agents/llm_backends/llm_backends.py
+++ b/agentfly/agents/llm_backends/llm_backends.py
@@ -17,8 +17,7 @@
from ...utils.verl import pad_tensor_to_rank_size
from vllm import LLM, AsyncLLMEngine, SamplingParams, AsyncEngineArgs
import openai
-from ..templates.templates import Chat
-from ..templates.vision_processor import get_processor
+from ...templates import Chat
import logging
import PIL
diff --git a/agentfly/agents/specialized/openai_agent.py b/agentfly/agents/specialized/openai_agent.py
index ae0f37a..38f725e 100644
--- a/agentfly/agents/specialized/openai_agent.py
+++ b/agentfly/agents/specialized/openai_agent.py
@@ -6,8 +6,8 @@
from ...tools import answer_qa
from ...tools.tool_base import tool
from ..agent_base import BaseAgent
-from ..llm_backend import ClientBackend
-from ..backend_config import ClientConfig
+from ..llm_backends import ClientBackend
+from ..llm_backends.backend_configs import ClientConfig
from tenacity import retry, wait_random_exponential, stop_after_attempt
from termcolor import colored
import json
diff --git a/agentfly/agents/templates/__init__.py b/agentfly/agents/templates/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/agentfly/templates/__init__.py b/agentfly/templates/__init__.py
new file mode 100644
index 0000000..d2b2176
--- /dev/null
+++ b/agentfly/templates/__init__.py
@@ -0,0 +1,10 @@
+from .templates import Template, Chat, get_template, register_template
+from .utils import (
+ process_vision_info,
+ tokenize_conversation,
+ tokenize_conversations,
+ compare_hf_template,
+)
+from .tool_policy import ToolPolicy, JsonFormatter
+from .system_policy import SystemPolicy
+from .global_policy import GlobalPolicy
\ No newline at end of file
diff --git a/agentfly/agents/templates/constants.py b/agentfly/templates/constants.py
similarity index 100%
rename from agentfly/agents/templates/constants.py
rename to agentfly/templates/constants.py
diff --git a/agentfly/templates/global_policy.py b/agentfly/templates/global_policy.py
new file mode 100644
index 0000000..1f3e013
--- /dev/null
+++ b/agentfly/templates/global_policy.py
@@ -0,0 +1,5 @@
+import dataclasses
+
+@dataclasses.dataclass
+class GlobalPolicy:
+ prefix: str = None
diff --git a/agentfly/agents/templates/preprocess.py b/agentfly/templates/preprocess.py
similarity index 100%
rename from agentfly/agents/templates/preprocess.py
rename to agentfly/templates/preprocess.py
diff --git a/agentfly/agents/templates/system_policy.py b/agentfly/templates/system_policy.py
similarity index 100%
rename from agentfly/agents/templates/system_policy.py
rename to agentfly/templates/system_policy.py
diff --git a/agentfly/agents/templates/templates.py b/agentfly/templates/templates.py
similarity index 93%
rename from agentfly/agents/templates/templates.py
rename to agentfly/templates/templates.py
index 6b8d3ea..7e54eff 100644
--- a/agentfly/agents/templates/templates.py
+++ b/agentfly/templates/templates.py
@@ -25,6 +25,7 @@
from .system_policy import Llama32DateProcessor, SystemPolicy
from .tool_policy import ToolPolicy
from .constants import ToolPlacement, Role
+from .global_policy import GlobalPolicy
Logger = logging.getLogger(__name__)
@@ -36,15 +37,25 @@
console_handler.setFormatter(formatter)
Logger.addHandler(console_handler)
-@dataclasses.dataclass
-class GlobalPolicy:
- prefix: str = None
-
@dataclasses.dataclass
class Template:
- """A class that manages prompt templates and keeps all conversation history."""
- # Properties
+ """Class that holds all the components of a chat template. Convert messages to string prompts, tokenize messages to token ids, and generate jinja-based chat templates.
+
+ Args:
+ name: The name of this template
+ system_template: The system template component
+ system_template_with_tools: The system template with tool usage component
+ system_message: The default system message
+ stop_words: The stop words where the model stops generating (usually EOS token)
+ tool_template: The tool response template component
+ user_template: The user template component
+ user_template_with_tools: The user template with tool usage component
+ assistant_template: The assistant template component
+ global_policy: The global policy, controls the behavior of the template
+ system_policy: The system message policy, controls the behavior of forming the system message
+ tool_policy: The tool policy for the template, controls the behavior of forming tools.
+ """
# The name of this template
name: str
# The template of the system prompt
@@ -142,10 +153,7 @@ def _supports_tool_call(self) -> bool:
return False
def render(self, messages: List[Dict], tools=None, add_generation_prompt: bool = False) -> str:
- """Render the template and return
- 1. the final prompt string,
- 2. the list of string *elements* that compose the prompt, and
- 3. the corresponding list of *roles* (used by downstream post-processing).
+ """Render the template.
The heavy lifting is delegated to small, single-purpose helpers so the
high-level flow is immediately apparent:
@@ -153,6 +161,16 @@ def render(self, messages: List[Dict], tools=None, add_generation_prompt: bool =
1. _insert_tools – decide where the tool catalogue lives
2. _encode_turns – encode every conversation turn
3. _maybe_add_generation_prompt – append the generation prefix if requested
+
+ Args:
+ messages: The list of messages
+ tools: The list of tools
+ add_generation_prompt: Whether to add the generation prefix
+
+ Returns:
+ prompt: The final prompt string
+ elements: The list of string *elements* that compose the prompt
+ roles: The corresponding list of *roles* (used by downstream post-processing)
"""
# Step 1 – decide tool placement & clone messages
@@ -173,15 +191,14 @@ def _insert_tools(self, messages: List[Dict], tools):
"""Clone *messages* and compute where (and how) the tool catalogue
should be injected.
- Returns
- -------
- work_messages : List[Dict]
- A deepcopy of the original *messages* so we never mutate caller data.
- tools_str : Optional[str]
- The formatted tool catalogue or *None* if `tools` is falsy.
- insert_tools_idx : int
- Index of the *user* message that receives the catalogue, or -1 when
- no injection is required.
+ Returns:
+ work_messages : List[Dict]
+ A deepcopy of the original *messages* so we never mutate caller data.
+ tools_str : Optional[str]
+ The formatted tool catalogue or *None* if `tools` is falsy.
+ insert_tools_idx : int
+ Index of the *user* message that receives the catalogue, or -1 when
+ no injection is required.
"""
work_messages = deepcopy(messages)
@@ -418,6 +435,19 @@ def _split_assistant_message(self, assistant_message: str) -> List[str]:
def encode(self, messages: List[Dict], tokenizer: PreTrainedTokenizer, return_tensors: str = None, tools=None, add_generation_prompt=False, processor=None) -> str:
+ """Encode the messages to token ids.
+
+ Args:
+ messages: The list of messages
+ tokenizer: The tokenizer
+ return_tensors: The return tensors
+ tools: The list of tools
+ add_generation_prompt: Whether to add the generation prefix
+ processor: The processor for vision templates
+
+ Returns:
+ inputs: The dictionary of input ids, attention mask, labels, and action mask
+ """
if processor is None and self.supports_vision():
raise ValueError(f"Processor is required for vision templates: {self.name}")
@@ -587,6 +617,11 @@ def get_vision_inputs(self, messages: List[Dict]):
return vision_inputs
def jinja_template(self) -> str:
+ """Interface for getting the Jinja template.
+
+ Returns:
+ The Jinja template string
+ """
if self.chat_template:
return self.chat_template
else:
@@ -926,6 +961,13 @@ def dict(self):
class Chat:
def __init__(self, template: str, messages: List[List[str]]=None, tools=None, tokenizer: PreTrainedTokenizer = None):
+ """
+ Args:
+ template: The name of the template to use.
+ messages: The messages to use for the chat.
+ tools: The tools to use for the chat.
+ tokenizer: The tokenizer to use for the chat.
+ """
self.template = get_template(template)
self.messages = self.convert_to_hf_format_messages(messages)
self.tokenizer = tokenizer
@@ -967,10 +1009,21 @@ def convert_to_hf_format_messages(self, messages: Union[List[Dict], Dict[str, Li
return hf_messages
def set_messages(self, messages: List[Dict]):
+ """Set the messages for the chat."""
self.messages = self.convert_to_hf_format_messages(messages)
def prompt(self, add_generation_prompt=False, tools=None) -> str:
+ """Get the prompt for the chat.
+
+ Args:
+ add_generation_prompt: Whether to add the generation prompt.
+ tools: The tools to use for the chat.
+
+ Returns:
+ The prompt for the chat.
+ """
self.flags['add_generation_prompt'] = add_generation_prompt
+ tools = tools or self.tools
prompt, _, _ = self.template.render(messages=self.messages, tools=tools, add_generation_prompt=add_generation_prompt)
return prompt
@@ -982,13 +1035,32 @@ def vision_inputs(self) -> List[Any]:
return self.template.get_vision_inputs(self.messages)
def tokenize(self, tokenizer: PreTrainedTokenizer = None, add_generation_prompt=False, tools=None, processor=None) -> List[int]:
+ """Tokenize the messages.
+
+ Args:
+ tokenizer: The tokenizer to use for the chat.
+ add_generation_prompt: Whether to add the generation prompt.
+ tools: The tools to use for the chat.
+ processor: The processor to use for the chat.
+
+ Returns:
+ inputs (dict): Inputs for helping training.
+ - input_ids
+ - attention_mask
+ - labels
+ - action_mask
+ - multi_modal_inputs
+ """
if tokenizer is None:
+ if self.tokenizer is None:
+ raise ValueError("Tokenizer is not set. Set it when initializing the chat or pass it as an argument.")
tokenizer = self.tokenizer
+
if tools is None:
tools = self.tools
return self.template.encode(messages=self.messages, tokenizer=tokenizer, return_tensors="pt", tools=tools, add_generation_prompt=add_generation_prompt, processor=processor)
- def append(self, message: Union[Dict, List[Dict]]):
+ def append(self, message: Union[Dict]):
self._convert_single_message_to_hf_format(message)
self.messages.append(message)
diff --git a/agentfly/agents/templates/tool_policy.py b/agentfly/templates/tool_policy.py
similarity index 100%
rename from agentfly/agents/templates/tool_policy.py
rename to agentfly/templates/tool_policy.py
diff --git a/agentfly/agents/templates/utils.py b/agentfly/templates/utils.py
similarity index 98%
rename from agentfly/agents/templates/utils.py
rename to agentfly/templates/utils.py
index ab6c07c..441f705 100644
--- a/agentfly/agents/templates/utils.py
+++ b/agentfly/templates/utils.py
@@ -10,10 +10,10 @@
import re
import logging
from .templates import Chat, get_template
-from ... import AGENT_DATA_DIR
+from .. import AGENT_DATA_DIR
from typing import Any
from .vision_processor import get_processor
-# Set up logging that won't be overridden by other modules
+
LOGGER = logging.getLogger(__name__)
ANSI_RE = re.compile(r'\x1b\[[0-9;]*m') # matches any ANSI color/style code
@@ -269,7 +269,8 @@ def tokenize_conversations(
concatenated_mm_inputs = {}
if concatenate_mm_inputs:
for key in batch_mm_inputs[0].keys():
- concatenated_mm_inputs[key] = torch.cat([mm_inputs[key] for mm_inputs in batch_mm_inputs if mm_inputs[key] is not None], dim=0)
+ if mm_inputs[key]:
+ concatenated_mm_inputs[key] = torch.cat([mm_inputs[key] for mm_inputs in batch_mm_inputs if mm_inputs[key] is not None], dim=0)
inputs = dict(
input_ids=batch_input_ids,
diff --git a/agentfly/agents/templates/vision_processor.py b/agentfly/templates/vision_processor.py
similarity index 100%
rename from agentfly/agents/templates/vision_processor.py
rename to agentfly/templates/vision_processor.py
diff --git a/agentfly/tests/unit/agents/templates/test_text_templates_full_align.py b/agentfly/tests/unit/agents/templates/test_text_templates_full_align.py
index 74cf1f3..652ffa4 100644
--- a/agentfly/tests/unit/agents/templates/test_text_templates_full_align.py
+++ b/agentfly/tests/unit/agents/templates/test_text_templates_full_align.py
@@ -8,7 +8,7 @@
"""
-from .....agents.templates.utils import compare_hf_template
+from .....templates import compare_hf_template
from transformers import AutoTokenizer
import pytest
diff --git a/agentfly/tools/tool_base.py b/agentfly/tools/tool_base.py
index 9129ffc..97047c2 100644
--- a/agentfly/tools/tool_base.py
+++ b/agentfly/tools/tool_base.py
@@ -13,6 +13,10 @@
import concurrent.futures
from ..envs.manager.env_manager import EnvironmentManager
+import logging
+
+logger = logging.getLogger(__name__)
+
# current_env = contextvars.ContextVar("current_env")
class Tool:
@@ -271,7 +275,8 @@ def decorator(func):
func_name = func.__name__
final_name = name or func_name
if name and name != func_name:
- warnings.warn(f"Tool name {func_name!r} overridden by {name!r}")
+ logger.warning(f"Tool name {func_name!r} overridden by {name!r}")
+ # warnings.warn(f"Tool name {func_name!r} overridden by {name!r}")
signature = extract_signatures(func)
docs = parse_docstring(inspect.getdoc(func))
diff --git a/agentfly/tools/utils/schema.py b/agentfly/tools/utils/schema.py
index 37016ce..2074178 100644
--- a/agentfly/tools/utils/schema.py
+++ b/agentfly/tools/utils/schema.py
@@ -2,6 +2,9 @@
import inspect
import warnings
from copy import deepcopy
+import logging
+
+logger = logging.getLogger(__name__)
def extract_signatures(func):
sig = inspect.signature(func)
@@ -153,7 +156,7 @@ def validate_schema(name, description, signature, docs):
else:
# May be should raise an error
properties[param]['type'] = "unknown"
- warnings.warn(f"Parameter {param} has no type in signature or docstring.")
+ logger.warning(f"Parameter {param} has no type in signature or docstring.")
if "default" in signature[param]:
properties[param]['default'] = signature[param]['default']
diff --git a/docs/api_references/agents/agent.rst b/docs/api_references/agents/agent.rst
index db85c3f..9ed2c3f 100644
--- a/docs/api_references/agents/agent.rst
+++ b/docs/api_references/agents/agent.rst
@@ -11,10 +11,4 @@ The foundation class for all agents in AgentFly:
:members:
:show-inheritance:
-Chain Generation
-----------------
-
-Base class for chain-based generation:
-.. autoclass:: agentfly.agents.chain.chain_base.ChainRollout
- :members:
diff --git a/docs/api_references/agents/index.rst b/docs/api_references/agents/index.rst
index 23c5f2c..853b2f1 100644
--- a/docs/api_references/agents/index.rst
+++ b/docs/api_references/agents/index.rst
@@ -9,7 +9,7 @@ Overview
AgentFly provides a comprehensive agent system with a base class and specialized implementations for different use cases. All agents inherit from :py:class:`BaseAgent` and support tool calling, chain rollout, and various backends.
-Base Agent
+Structure
==========
.. toctree::
@@ -17,97 +17,8 @@ Base Agent
agent
llm_backends
+ rollout
-Core Classes
-===========
-
-BaseAgent
----------
-
-The foundation class for all agents in AgentFly:
-
-.. autoclass:: agentfly.agents.agent_base.BaseAgent
- :members:
- :show-inheritance:
- :special-members: __init__
-
-AutoAgent
----------
-
-Factory class for automatic agent creation:
-
-.. autoclass:: agentfly.agents.auto.AutoAgent
- :members:
- :show-inheritance:
-
-Specialized Agents
-==================
-
-ReactAgent
-----------
-
-ReAct-style agent for reasoning and tool use:
-
-.. autoclass:: agentfly.agents.react.react_agent.ReactAgent
- :members:
- :show-inheritance:
-
-CodeAgent
----------
-
-Specialized agent for code generation and execution:
-
-.. autoclass:: agentfly.agents.specialized.code_agent.CodeAgent
- :members:
- :show-inheritance:
-
-ThinkAgent
-----------
-
-Agent that uses thinking steps before taking actions:
-
-.. autoclass:: agentfly.agents.specialized.think_agent.ThinkAgent
- :members:
- :show-inheritance:
-
-GUIAgent
----------
-
-Agent for GUI automation tasks:
-
-.. autoclass:: agentfly.agents.specialized.gui_agent.GUIAgent
- :members:
- :show-inheritance:
-
-HFAgent
---------
-
-Hugging Face model-based agent:
-
-.. autoclass:: agentfly.agents.specialized.hf_agent.HFAgent
- :members:
- :show-inheritance:
-
-OpenAIAgent
------------
-
-OpenAI API-based agent:
-
-.. autoclass:: agentfly.agents.specialized.openai_agent.OpenAIAgent
- :members:
- :show-inheritance:
-
-Chain Generation
-===============
-
-ChainRollout
-------------
-
-Base class for chain-based generation:
-
-.. autoclass:: agentfly.agents.chain.chain_base.ChainRollout
- :members:
- :show-inheritance:
Usage Examples
=============
diff --git a/docs/api_references/agents/llm_backends.rst b/docs/api_references/agents/llm_backends.rst
index 8f57918..ef0d7bb 100644
--- a/docs/api_references/agents/llm_backends.rst
+++ b/docs/api_references/agents/llm_backends.rst
@@ -1,27 +1,19 @@
LLM Backends
-============
+============================
Overview
---------
+-----------------------------
AgentFly supports multiple LLM backends for text generation, each with their own configuration options.
-This module provides configuration classes for different backend types including Transformers, vLLM, Verl, and OpenAI-compatible clients.
+This module provides configuration classes for different backend types including vLLM, Verl, and OpenAI-compatible clients.
+Among them, Verl backend is designed for internal training usage. The Verl backend is the core design that **decouples agent system and rl training**.
Configuration Classes
---------------------
-
-Transformers Backend
-~~~~~~~~~~~~~~~~~~~
-
-Configuration for the Transformers backend using Hugging Face models:
-
-.. autoclass:: agentfly.agents.llm_backends.backend_configs.TransformersConfig
- :show-inheritance:
- :special-members: !__init__
+-----------------------------
Async VLLM Backend
-~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Configuration for asynchronous vLLM backend with engine arguments:
@@ -31,7 +23,7 @@ Configuration for asynchronous vLLM backend with engine arguments:
Async Verl Backend
-~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Configuration for asynchronous Verl backend:
@@ -40,7 +32,7 @@ Configuration for asynchronous Verl backend:
:special-members: !__init__
Client Backend
-~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Configuration for OpenAI-compatible client backends:
@@ -49,10 +41,10 @@ Configuration for OpenAI-compatible client backends:
:special-members: !__init__
Backend Implementations
-----------------------
+------------------------------
Base Backend
-~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Abstract base class for all LLM backends:
@@ -60,26 +52,9 @@ Abstract base class for all LLM backends:
:members:
:show-inheritance:
-Transformers Backend
-~~~~~~~~~~~~~~~~~~~
-
-HuggingFace Transformers implementation for local model inference:
-
-.. autoclass:: agentfly.agents.llm_backends.llm_backends.TransformersBackend
- :members:
- :show-inheritance:
-
-VLLM Backend
-~~~~~~~~~~~~
-
-vLLM implementation for high-performance model inference:
-
-.. autoclass:: agentfly.agents.llm_backends.llm_backends.VLLMBackend
- :members:
- :show-inheritance:
Async VLLM Backend
-~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Asynchronous vLLM implementation for high-performance model inference:
@@ -97,7 +72,7 @@ Asynchronous Verl implementation for distributed model inference:
:show-inheritance:
Client Backend
-~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OpenAI-compatible client backend for remote API inference:
@@ -107,13 +82,13 @@ OpenAI-compatible client backend for remote API inference:
Usage Examples
---------------
+------------------------------
Backends are designed to work together with agents. Here are examples showing how to configure different backends when creating agents:
Async VLLM Backend
-~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
diff --git a/docs/api_references/agents/rollout.rst b/docs/api_references/agents/rollout.rst
new file mode 100644
index 0000000..3bfb6dc
--- /dev/null
+++ b/docs/api_references/agents/rollout.rst
@@ -0,0 +1,11 @@
+Rollout
+=======
+
+
+Chain Generation
+----------------
+
+Base class for chain-based generation:
+
+.. autoclass:: agentfly.agents.chain.chain_base.ChainRollout
+ :members:
\ No newline at end of file
diff --git a/docs/features/chat_template/README.md b/docs/api_references/chat_template/README.md
similarity index 100%
rename from docs/features/chat_template/README.md
rename to docs/api_references/chat_template/README.md
diff --git a/docs/features/chat_template/advanced_features.md b/docs/api_references/chat_template/advanced_features.md
similarity index 96%
rename from docs/features/chat_template/advanced_features.md
rename to docs/api_references/chat_template/advanced_features.md
index d4a1bff..cd7ede5 100644
--- a/docs/features/chat_template/advanced_features.md
+++ b/docs/api_references/chat_template/advanced_features.md
@@ -11,7 +11,7 @@ The Chat Template System provides advanced features for fine-grained control ove
The system supports multiple strategies for where and how tools are integrated into prompts:
```python
-from agentfly.agents.templates.constants import ToolPlacement
+from agentfly.templates.constants import ToolPlacement
# 1. SYSTEM placement - tools appear in system message
system_placement = ToolPlacement.SYSTEM
@@ -31,7 +31,7 @@ Different strategies for formatting tool definitions:
#### JSON Formatters
```python
-from agentfly.agents.templates.tool_policy import (
+from agentfly.templates.tool_policy import (
JsonFormatter, JsonMinifiedFormatter, JsonIndentedFormatter, JsonCompactFormatter
)
@@ -65,7 +65,7 @@ yaml_formatter = YamlFormatter()
#### Custom Formatters
```python
-from agentfly.agents.templates.tool_policy import ToolFormatter
+from agentfly.templates.tool_policy import ToolFormatter
class CustomToolFormatter(ToolFormatter):
def format(self, tools):
@@ -101,7 +101,7 @@ custom_tool_policy = ToolPolicy(
Process tool content before formatting:
```python
-from agentfly.agents.templates.tool_policy import ToolContentProcessor
+from agentfly.templates.tool_policy import ToolContentProcessor
class ToolFilterProcessor(ToolContentProcessor):
"""Filter tools based on certain criteria"""
@@ -139,7 +139,7 @@ filtered_tool_policy = ToolPolicy(
Fine-grained control over system message behavior:
```python
-from agentfly.agents.templates.system_policy import SystemPolicy
+from agentfly.templates.system_policy import SystemPolicy
# Basic system policy
basic_policy = SystemPolicy(
@@ -170,7 +170,7 @@ Transform system messages before rendering:
#### Built-in Processors
```python
-from agentfly.agents.templates.system_policy import Llama32DateProcessor
+from agentfly.templates.system_policy import Llama32DateProcessor
# Llama 3.2 date processor (adds current date)
llama_date_policy = SystemPolicy(
@@ -183,7 +183,7 @@ llama_date_policy = SystemPolicy(
#### Custom Content Processors
```python
-from agentfly.agents.templates.system_policy import SystemContentProcessor
+from agentfly.templates.system_policy import SystemContentProcessor
class EnvironmentAwareProcessor(SystemContentProcessor):
"""Add environment information to system messages"""
diff --git a/docs/features/chat_template/basic_usage.md b/docs/api_references/chat_template/basic_usage.md
similarity index 74%
rename from docs/features/chat_template/basic_usage.md
rename to docs/api_references/chat_template/basic_usage.md
index b376374..38b550b 100644
--- a/docs/features/chat_template/basic_usage.md
+++ b/docs/api_references/chat_template/basic_usage.md
@@ -7,9 +7,7 @@ The Chat Template System provides a simple yet powerful interface for creating a
## Importing the System
```python
-from agentfly.agents.templates import Chat, get_template, Template
-from agentfly.agents.templates.tool_policy import ToolPolicy, JsonFormatter
-from agentfly.agents.templates.system_policy import SystemPolicy
+from agentfly.templates import Chat, get_template, Template, ToolPolicy, JsonFormatter, SystemPolicy
```
## Using Pre-built Templates
@@ -28,6 +26,8 @@ The system comes with several pre-built templates:
### Basic Template Usage
+`Template` is the basic template class, consists of different components and responsible for forming the prompt. While `Chat` is the class we recommand for users to obtain prompts.
+
```python
# Get a pre-built template
template = get_template("qwen2.5")
@@ -91,12 +91,13 @@ messages_with_image = [
"role": "user",
"content": [
{"type": "text", "text": "What's in this image?"},
- {"type": "image", "image": "/path/to/image.jpg"}
+ {"type": "image", "image": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"}
]
}
]
chat = Chat(template="qwen2.5-vl", messages=messages_with_image)
+prompt = chat.prompt()
```
## Template Operations
@@ -116,13 +117,21 @@ prompt_with_tools = chat.prompt(tools=tools)
### Tokenization
+Use `Chat.tokenize` method to tokenize the messages with the specified chat template.
+
```python
+from transformers import AutoTokenizer, AutoProcessor
+
+tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-VL-3B-Instruct")
+processor = AutoProcessor.from_pretrained("Qwen/Qwen2.5-VL-3B-Instruct")
# Tokenize the conversation
inputs = chat.tokenize(
tokenizer=tokenizer,
add_generation_prompt=True,
+ processor=processor,
tools=tools
)
+print(inputs.keys())
# The result includes:
# - input_ids: Token IDs
@@ -136,12 +145,6 @@ inputs = chat.tokenize(
```python
# Add a single message
chat.append({"role": "user", "content": "Another question"})
-
-# Add multiple messages
-chat.append([
- {"role": "user", "content": "Question 1"},
- {"role": "assistant", "content": "Answer 1"}
-])
```
## Template Configuration
@@ -269,57 +272,3 @@ tools = [
"content": "Search results: [results here]"
}
```
-
-## Error Handling
-
-### Common Issues
-
-```python
-try:
- # Get a template that doesn't exist
- template = get_template("nonexistent")
-except KeyError as e:
- print(f"Template not found: {e}")
-
-try:
- # Create chat with invalid template
- chat = Chat(template="invalid", messages=messages)
-except KeyError as e:
- print(f"Invalid template: {e}")
-```
-
-### Validation
-
-```python
-# Check if template supports vision
-if template.supports_vision():
- print("Template supports vision processing")
-
-# Check if template supports tool calls
-if template._supports_tool_call():
- print("Template supports tool calls")
-```
-
-## Best Practices
-
-### 1. **Template Naming**
-- Use descriptive names that indicate the model and capabilities
-- Include version information when appropriate
-- Use consistent naming conventions
-
-### 2. **Message Structure**
-- Always use the standard role/content format
-- For multi-modal content, use the list format with type specifications
-- Ensure content types match the template's capabilities
-
-### 3. **Tool Integration**
-- Define tools with clear, descriptive names and parameters
-- Use appropriate tool placement strategies for your use case
-- Test tool integration thoroughly before deployment
-
-### 4. **Vision Processing**
-- Use vision-enabled templates for image/video content
-- Ensure proper image formats and sizes
-- Handle vision token expansion appropriately
-
-This basic usage guide should get you started with the Chat Template System. For more advanced features, see the [Advanced Features](./advanced_features.md) and [Vision Templates](./vision_templates.md) sections.
diff --git a/docs/api_references/chat_template/chat.rst b/docs/api_references/chat_template/chat.rst
new file mode 100644
index 0000000..f790ca1
--- /dev/null
+++ b/docs/api_references/chat_template/chat.rst
@@ -0,0 +1,8 @@
+Chat
+======================
+
+
+.. autoclass:: agentfly.templates.Chat
+ :members:
+ :show-inheritance:
+
diff --git a/docs/api_references/chat_template/core_components.md b/docs/api_references/chat_template/core_components.md
new file mode 100644
index 0000000..f367c56
--- /dev/null
+++ b/docs/api_references/chat_template/core_components.md
@@ -0,0 +1,175 @@
+
+
+
+
+
+
+# Core Components
+
+### Core Chat Template Components
+
+The Chat Template System is inspired by the art of building block toys - where complex structures are created by combining simple, standardized components. We identify some basic components from LLM's chat templates, and use them to form prompts from conversation messages. Below are some basic core compoenents:
+
+`system_template`: Specify how system prompt is formatted in chat template.
+
+`system_template_with_tools`: Specify how tools along with system prompt is formatted in chat template
+
+`user_template`: Specify how user message is formatted in chat template
+
+`assistant_template`: Specify how assistant is formatted in chat template
+
+`tool_template`: Specify how tool response is formatted in chat template
+
+Assume we have the following chat template, and messages
+```
+system_template = f"System: {system_message}\n"
+system_template_with_tools = f"System: {system_message}\n#Tools: {tools}\n"
+user_template = "User: {content}\n"
+assistant_template = "User: {content}\n"
+
+messages = [
+ {"role": "system", "content": "You are a helpful assistant."},
+ {"role": "user", "content": "Hi, Can you help me search the information."},
+ {"role": "assistant", "content": "tool call: search tool arguments: related query"}
+ {"role": "tool", "content": "Searched inforamtion..."}
+]
+
+tools = [
+ {
+ "name": "search",
+ "description": "Search the web."
+ }
+]
+```
+
+**Formatted Prompt**
+
+ : formatted system prompt; : formatted user message; : formatted assistant message; : formatted tool message;
+
+1. When combined, these create the complete prompt:
+
+System: You are a helpful assistant.
+
+User: Hi, Can you help me search the information.
+
+Assistant: tool call: search\ntool arguments: related query
+
+Tool: Searched inforamtion...
+
+2. When tools are included, the `system_template_with_tools` is used:
+
+System: You are a helpful assistant.
+
+#Tools: [{"name": "search", "description": "Search the web"}]
+
+User: Hi, Can you help me search the information.
+
+Assistant: tool call: search\ntool arguments: related query
+
+Tool: Searched inforamtion...
+
+
+### High-Level Workflow
+
+```
+Messages + Tools → Template Processing → Vision Processing → LLM-Ready Inputs
+```
+
+The system follows a three-step rendering process:
+
+1. **Tool Insertion**: Decide where and how to inject tool definitions
+2. **Turn Encoding**: Convert each conversation turn to its textual representation
+3. **Generation Prompt**: Optionally append generation prefixes
+
+If we tokenize the input messages, the vision processor will do the following steps:
+
+- **Template** → Human-readable prompt with vision tokens
+- **Vision Processor** → Token expansion and multi-modal inputs
+- **Result** → LLM-ready inputs with proper tensor alignment
+
+### Core Class Components
+
+#### Template
+The central class that manages:
+- Message formatting templates
+- Policy configurations
+- Jinja template generation
+
+#### Chat
+Recommended class for user usage:
+- Store and format messages
+- Get formatted prompts
+- Tokenize formatted prompt
+
+### Advanced Features
+
+**1. Register & Obtain Template**
+
+Templates are created and retrieved through a global registry:
+```python
+# Registration
+register_template(Template(name="custom", ...))
+
+# Retrieval
+template = get_template("custom")
+```
+
+**2. Fine-grained Behavior Control**
+
+Three levels of policy control:
+
+1. **Global Policy**: Template-wide settings (e.g., prefix tokens)
+2. **System Policy**: System message behavior and content processing
+3. **Tool Policy**: Tool placement, formatting, and content processing
+
+```python
+# Tool formatting strategies
+JsonFormatter(indent=4)
+JsonCompactFormatter()
+YamlFormatter()
+
+# Tool placement strategies
+ToolPlacement.SYSTEM
+ToolPlacement.FIRST_USER
+ToolPlacement.LAST_USER
+```
+
+**3. Vision Process**
+
+Vision processors are automatically registered when vision tokens are detected:
+```python
+def _register_vision_processor(self):
+ """Automatically register a vision processor for this template"""
+ if self.image_token or self.video_token:
+ # Auto-registration based on template configuration
+```
+
+
+**4. Jinja Template Generation**
+
+Templates can generate HuggingFace-compatible Jinja templates:
+- Enables use with external systems (vLLM, transformers tokenizers, etc.)
+- Maintains consistency between Python and Jinja rendering
+- Supports complex logic through Jinja macros
diff --git a/docs/features/chat_template/custom_templates.md b/docs/api_references/chat_template/custom_templates.md
similarity index 65%
rename from docs/features/chat_template/custom_templates.md
rename to docs/api_references/chat_template/custom_templates.md
index b390515..8ec10c5 100644
--- a/docs/features/chat_template/custom_templates.md
+++ b/docs/api_references/chat_template/custom_templates.md
@@ -8,18 +8,48 @@ The Chat Template System is designed to be highly extensible, allowing you to cr
### Core Template Fields
-```python
-from agentfly.agents.templates import Template
-template = Template(
- name="my-custom-template", # Unique identifier
- system_template="{system_message}", # System message format
- system_message="Default system message",
- user_template="{content}", # User message format
- assistant_template="{content}", # Assistant message format
- tool_template="{observation}", # Tool response format
- stop_words=[""] # Stop generation tokens
+
+```python
+from agentfly.templates import Template, register_template, Chat
+
+register_template(
+ Template(
+ name="my-custom-template", # Unique identifier
+ system_template="System: {system_message}", # System message format
+ system_message="You are a helpful assistant.", # Default system message
+ user_template="User: {content}", # User message format
+ assistant_template="Assistant: {content}", # Assistant message format
+ tool_template="Tool: {observation}", # Tool response format
+ stop_words=[""] # Stop generation tokens
+ )
)
+
+messages = [
+ {"role": "user", "content": "What is the capital of France?"},
+ {"role": "assistant", "content": "The capital of France is Paris."},
+ {"role": "user", "content": "Tell me more about Paris."}
+]
+
+tools = [
+ {
+ "function": {
+ "name": "get_weather",
+ "description": "Get weather information for a city",
+ "parameters": {
+ "type": "object",
+ "properties": {
+ "city": {"type": "string", "description": "City name"}
+ },
+ "required": ["city"]
+ }
+ }
+ }
+]
+
+chat = Chat(template="my-custom-template", messages=messages, tools=tools)
+prompt = chat.prompt()
+print(prompt)
```
### Advanced Template Fields
@@ -30,7 +60,6 @@ template = Template(
# Tool support
system_template_with_tools="System: {system_message}\n\nTools: {tools}",
- user_template_with_tools="User: {content}\n\nAvailable tools: {tools}",
# Vision support
vision_start="",
@@ -48,73 +77,139 @@ template = Template(
### 1. Simple Chat Template
```python
-simple_template = Template(
- name="simple-chat",
- system_template="You are a helpful assistant.\n",
- system_message="You are a helpful assistant.",
- user_template="User: {content}\n",
- assistant_template="Assistant: {content}\n",
- stop_words=["\n"]
+register_template(
+ Template(
+ name="simple-chat",
+ system_template="You are a helpful assistant.\n",
+ system_message="You are a helpful assistant.",
+ user_template="User: {content}\n",
+ assistant_template="Assistant: {content}\n",
+ stop_words=["\n"]
+ )
)
+chat = Chat(template="simple-chat", messages=messages, tools=tools)
+print(chat.prompt())
```
### 2. XML-Style Template
```python
-xml_template = Template(
- name="xml-style",
- system_template="{system_message}\n",
- system_message="You are an AI assistant.",
- user_template="{content}\n",
- assistant_template="{content}\n",
- stop_words=[""]
+register_template(
+ Template(
+ name="xml-style",
+ system_template="{system_message}\n",
+ system_message="You are an AI assistant.",
+ user_template="{content}\n",
+ assistant_template="{content}\n",
+ stop_words=[""]
+ )
)
+chat = Chat(template="xml-style", messages=messages, tools=tools)
+print(chat.prompt())
```
### 3. Markdown-Style Template
```python
-markdown_template = Template(
- name="markdown-style",
- system_template="# System\n{system_message}\n\n",
- system_message="You are a helpful AI assistant.",
- user_template="## User\n{content}\n\n",
- assistant_template="## Assistant\n{content}\n\n",
- stop_words=["\n\n"]
+register_template(
+ Template(
+ name="markdown-style",
+ system_template="# System\n{system_message}\n\n",
+ system_message="You are a helpful AI assistant.",
+ user_template="## User\n{content}\n\n",
+ assistant_template="## Assistant\n{content}\n\n",
+ stop_words=["\n\n"]
+ )
)
+chat = Chat(template="markdown-style", messages=messages, tools=tools)
+print(chat.prompt())
```
### 4. Tool-Enabled Template
```python
-tool_template = Template(
- name="tool-enabled",
- system_template="System: {system_message}\n",
- system_template_with_tools="System: {system_message}\n\nAvailable Tools:\n{tools}\n",
- system_message="You are an AI assistant with access to tools.",
- user_template="User: {content}\n",
- user_template_with_tools="User: {content}\n\nTools: {tools}\n",
- assistant_template="Assistant: {content}\n",
- tool_template="Tool Response: {observation}\n",
- stop_words=["\n"]
+register_template(
+ Template(
+ name="tool-enabled",
+ system_template="System: {system_message}\n",
+ system_template_with_tools="System: {system_message}\n\nAvailable Tools:\n{tools}\n",
+ system_message="You are an AI assistant with access to tools.",
+ user_template="User: {content}\n",
+ user_template_with_tools="User: {content}\n\nTools: {tools}\n",
+ assistant_template="Assistant: {content}\n",
+ tool_template="Tool Response: {observation}\n",
+ stop_words=["\n"]
+ )
)
+messages = [
+ {"role": "user", "content": "Find me the weather of Paris"},
+ {"role": "assistant", "content": "Tool: get_weather Arguments: {'city': 'Paris'}"},
+ {"role": "Tool", "content": "24 degrees, raining."}
+]
+tools = [
+ {
+ "function": {
+ "name": "get_weather",
+ "description": "Get weather information for a city",
+ "parameters": {
+ "type": "object",
+ "properties": {
+ "city": {"type": "string", "description": "City name"}
+ },
+ "required": ["city"]
+ }
+ }
+ }
+]
+chat = Chat(template="tool-enabled", messages=messages, tools=tools)
+print(chat.prompt())
```
### 5. Vision-Enabled Template
```python
-vision_template = Template(
- name="vision-enabled",
- system_template="You are a vision-capable AI assistant.\n",
- system_message="You are a vision-capable AI assistant.",
- user_template="User: {content}\n",
- assistant_template="Assistant: {content}\n",
- vision_start="",
- vision_end="",
- image_token="",
- video_token="