Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into swarmsysmsgfunc
Browse files Browse the repository at this point in the history
  • Loading branch information
marklysze committed Nov 30, 2024
2 parents 12b0bbe + 5ab5522 commit 8fddf90
Show file tree
Hide file tree
Showing 20 changed files with 762 additions and 136 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/contrib-graph-rag-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
run: |
python -m pip install --upgrade pip wheel
pip install pytest
- name: Install Falkor DB SDK when on linux
- name: Install FalkorDB SDK when on linux
run: |
pip install -e .[graph_rag_falkor_db]
- name: Set AUTOGEN_USE_DOCKER based on OS
Expand Down
1 change: 1 addition & 0 deletions MAINTAINERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
| Aaron Ward | [AaronWard](https://github.com/AaronWard) | yappstore.ai | all |
| Rudy Wu | [rudyalways](https://github.com/rudyalways) | Google | all, group chats, sequential chats |
| Haiyang Li | [ohdearquant](https://github.com/ohdearquant) | - | all, sequential chats, structured output, low-level|
| Eric Moore | [emooreatx](https://github.com/emooreatx) | IBM | all|

**Pending Maintainers list (Marked with \*, Waiting for explicit approval from the maintainers)**
| Name | GitHub Handle | Organization | Features |
Expand Down
24 changes: 9 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<a name="readme-top"></a>

![Pypi Downloads](https://img.shields.io/pypi/dm/pyautogen?label=PyPI%20downloads)
[![PyPI version](https://badge.fury.io/py/autogen.svg)](https://badge.fury.io/py/autogen)
[![Build](https://github.com/ag2ai/ag2/actions/workflows/python-package.yml/badge.svg)](https://github.com/ag2ai/ag2/actions/workflows/python-package.yml)
![Python Version](https://img.shields.io/badge/3.8%20%7C%203.9%20%7C%203.10%20%7C%203.11%20%7C%203.12-blue)
Expand All @@ -8,24 +9,20 @@

<!-- [![NuGet version](https://badge.fury.io/nu/AutoGen.Core.svg)](https://badge.fury.io/nu/AutoGen.Core) -->


<a name="readme-top"></a>

<div align="center">
<img src="./website/static/img/ag2.png" alt="AutoGen Logo" width="100">


</div>
# [AG2](https://github.com/ag2ai/ag2)

[📚 Cite paper](#related-papers).
<!-- <p align="center">
<img src="https://github.com/ag2ai/ag2/blob/main/website/static/img/flaml.svg" width=200>
<br>
</p> -->

> [!IMPORTANT]
> **:tada: IMPORTANT**
>
> :fire: :tada: **Nov 11, 2024:** We are evolving AutoGen into **AG2**!
> A new organization [AG2ai](https://github.com/ag2ai) is created to host the development of AG2 and related projects with open governance. Check [AG2's new look](https://ag2.ai/).
>
> :fire: :tada: Nov 11, 2024: We are evolving AutoGen into AG2! A new organization [ag2ai](https://github.com/ag2ai) is created to host the development of AG2 and related projects with open governance. We invite collaborators from all organizations and individuals to join the development.
> We invite collaborators from all organizations and individuals to join the development.

:fire: :tada: AG2 is available via `ag2` (or its alias `autogen` or `pyautogen`) on PyPI! Starting with version 0.3.2, you can now install AG2 using:
Expand Down Expand Up @@ -98,8 +95,7 @@ AG2 (formerly AutoGen) is an open-source programming framework for building AI a
The project is currently maintained by a [dynamic group of volunteers](MAINTAINERS.md) from several organizations. Contact project administrators Chi Wang and Qingyun Wu via [support@ag2.ai](mailto:support@ag2.ai) if you are interested in becoming a maintainer.


![AutoGen Overview](./website/static/img/autogen_agentchat.png)

![AutoGen Overview](https://media.githubusercontent.com/media/ag2ai/ag2/refs/heads/main/website/static/img/autogen_agentchat.png)


<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
Expand Down Expand Up @@ -199,7 +195,7 @@ python test/twoagent.py
After the repo is cloned.
The figure below shows an example conversation flow with AG2.

![Agent Chat Example](./website/static/img/chat_example.png)
![Agent Chat Example](https://media.githubusercontent.com/media/ag2ai/ag2/refs/heads/main/website/static/img/chat_example.png)


Alternatively, the [sample code](https://github.com/ag2ai/build-with-ag2/blob/main/samples/simple_chat.py) here allows a user to chat with an AG2 agent in ChatGPT style.
Expand Down Expand Up @@ -252,8 +248,6 @@ In addition, you can find:

- [Contributing guide](https://ag2ai.github.io/ag2/docs/Contribute)

- [Roadmap](https://ag2.ai/#roadmap) and [Roadmap Issues](https://github.com/ag2ai/ag2/issues?q=is%3Aopen+is%3Aissue+label%3Aroadmap)

<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: blue; font-weight: bold;">
↑ Back to Top ↑
Expand Down
Empty file.
75 changes: 52 additions & 23 deletions autogen/agentchat/contrib/graph_rag/falkor_graph_query_engine.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
# Copyright (c) 2023 - 2024, Owners of https://github.com/ag2ai
#
# SPDX-License-Identifier: Apache-2.0
#
# Portions derived from https://github.com/microsoft/autogen are under the MIT License.
# SPDX-License-Identifier: MIT

import os
from dataclasses import field
from dataclasses import dataclass, field
from typing import List

from graphrag_sdk import KnowledgeGraph, Source
from graphrag_sdk.schema import Schema
from graphrag_sdk.model_config import KnowledgeGraphModelConfig
from graphrag_sdk.models import GenerativeModel
from graphrag_sdk.models.openai import OpenAiGenerativeModel
from graphrag_sdk.ontology import Ontology

from .document import Document
from .graph_query_engine import GraphStoreQueryResult


class FalkorGraphQueryResult(GraphStoreQueryResult):
messages: list = field(default_factory=list)


class FalkorGraphQueryEngine:
"""
This is a wrapper for Falkor DB KnowledgeGraph.
This is a wrapper for FalkorDB KnowledgeGraph.
"""

def __init__(
Expand All @@ -31,11 +28,11 @@ def __init__(
port: int = 6379,
username: str | None = None,
password: str | None = None,
model: str = "gpt-4-1106-preview",
schema: Schema | None = None,
model: GenerativeModel = OpenAiGenerativeModel("gpt-4o"),
ontology: Ontology | None = None,
):
"""
Initialize a Falkor DB knowledge graph.
Initialize a FalkorDB knowledge graph.
Please also refer to https://github.com/FalkorDB/GraphRAG-SDK/blob/main/graphrag_sdk/kg.py
Args:
Expand All @@ -44,11 +41,18 @@ def __init__(
port (int): FalkorDB port number.
username (str|None): FalkorDB username.
password (str|None): FalkorDB password.
model (str): OpenAI model to use for Falkor DB to build and retrieve from the graph.
schema: Falkor DB knowledge graph schema (ontology), https://github.com/FalkorDB/GraphRAG-SDK/blob/main/graphrag_sdk/schema/schema.py
If None, Falkor DB will auto generate a schema from the input docs.
model (GenerativeModel): LLM model to use for FalkorDB to build and retrieve from the graph, default to use OAI gpt-4o.
ontology: FalkorDB knowledge graph schema/ontology, https://github.com/FalkorDB/GraphRAG-SDK/blob/main/graphrag_sdk/ontology.py
If None, FalkorDB will auto generate an ontology from the input docs.
"""
self.knowledge_graph = KnowledgeGraph(name, host, port, username, password, model, schema)
self.name = name
self.host = host
self.port = port
self.username = username
self.password = password
self.model = model
self.model_config = KnowledgeGraphModelConfig.with_model(model)
self.ontology = ontology

def init_db(self, input_doc: List[Document] | None):
"""
Expand All @@ -60,14 +64,33 @@ def init_db(self, input_doc: List[Document] | None):
sources.append(Source(doc.path_or_url))

if sources:
# Auto generate graph ontology if not created by user.
if self.ontology is None:
self.ontology = Ontology.from_sources(
sources=sources,
model=self.model,
)

self.knowledge_graph = KnowledgeGraph(
name=self.name,
host=self.host,
port=self.port,
username=self.username,
password=self.password,
model_config=KnowledgeGraphModelConfig.with_model(self.model),
ontology=self.ontology,
)

# Establish a chat session, this will maintain the history
self._chat_session = self.knowledge_graph.chat_session()
self.knowledge_graph.process_sources(sources)

def add_records(self, new_records: List) -> bool:
raise NotImplementedError("This method is not supported by Falkor DB SDK yet.")
raise NotImplementedError("This method is not supported by FalkorDB SDK yet.")

def query(self, question: str, n_results: int = 1, **kwargs) -> FalkorGraphQueryResult:
def query(self, question: str, n_results: int = 1, **kwargs) -> GraphStoreQueryResult:
"""
Query the knowledage graph with a question and optional message history.
Query the knowledge graph with a question and optional message history.
Args:
question: a human input question.
Expand All @@ -77,6 +100,12 @@ def query(self, question: str, n_results: int = 1, **kwargs) -> FalkorGraphQuery
Returns: FalkorGraphQueryResult
"""
messages = kwargs.pop("messages", [])
answer, messages = self.knowledge_graph.ask(question, messages)
return FalkorGraphQueryResult(answer=answer, results=[], messages=messages)
if self.knowledge_graph is None:
raise ValueError("Knowledge graph is not created.")

response = self._chat_session.send_message(question)

# History will be considered when querying by setting the last_answer
self._chat_session.last_answer = response["response"]

return GraphStoreQueryResult(answer=response["response"], results=[])
81 changes: 81 additions & 0 deletions autogen/agentchat/contrib/graph_rag/falkor_graph_rag_capability.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Copyright (c) 2023 - 2024, Owners of https://github.com/ag2ai
#
# SPDX-License-Identifier: Apache-2.0

from typing import Any, Dict, List, Optional, Tuple, Union

from autogen import Agent, ConversableAgent, UserProxyAgent

from .falkor_graph_query_engine import FalkorGraphQueryEngine
from .graph_query_engine import GraphStoreQueryResult
from .graph_rag_capability import GraphRagCapability


class FalkorGraphRagCapability(GraphRagCapability):
"""
The FalkorDB GraphRAG capability integrate FalkorDB with graphrag_sdk version: 0.1.3b0.
Ref: https://github.com/FalkorDB/GraphRAG-SDK/tree/2-move-away-from-sql-to-json-ontology-detection
For usage, please refer to example notebook/agentchat_graph_rag_falkordb.ipynb
"""

def __init__(self, query_engine: FalkorGraphQueryEngine):
"""
initialize GraphRAG capability with a graph query engine
"""
self.query_engine = query_engine

def add_to_agent(self, agent: UserProxyAgent):
"""
Add FalkorDB GraphRAG capability to a UserProxyAgent.
The restriction to a UserProxyAgent to make sure the returned message does not contain information retrieved from the graph DB instead of any LLMs.
"""
self.graph_rag_agent = agent

# Validate the agent config
if agent.llm_config not in (None, False):
raise Exception(
"Agents with GraphRAG capabilities do not use an LLM configuration. Please set your llm_config to None or False."
)

# Register method to generate the reply using a FalkorDB query
# All other reply methods will be removed
agent.register_reply(
[ConversableAgent, None], self._reply_using_falkordb_query, position=0, remove_other_reply_funcs=True
)

def _reply_using_falkordb_query(
self,
recipient: ConversableAgent,
messages: Optional[List[Dict]] = None,
sender: Optional[Agent] = None,
config: Optional[Any] = None,
) -> Tuple[bool, Union[str, Dict, None]]:
"""
Query FalkorDB and return the message. Internally, it utilises OpenAI to generate a reply based on the given messages.
The history with FalkorDB is also logged and updated.
If no results are found, a default message is returned: "I'm sorry, I don't have an answer for that."
Args:
recipient: The agent instance that will receive the message.
messages: A list of messages in the conversation history with the sender.
sender: The agent instance that sent the message.
config: Optional configuration for message processing.
Returns:
A tuple containing a boolean indicating success and the assistant's reply.
"""
question = self._get_last_question(messages[-1])
result: GraphStoreQueryResult = self.query_engine.query(question)

return True, result.answer if result.answer else "I'm sorry, I don't have an answer for that."

def _get_last_question(self, message: Union[Dict, str]):
"""Retrieves the last message from the conversation history."""
if isinstance(message, str):
return message
if isinstance(message, Dict):
if "content" in message:
return message["content"]
return None
2 changes: 1 addition & 1 deletion autogen/agentchat/contrib/graph_rag/graph_query_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class GraphStoreQueryResult:
class GraphQueryEngine(Protocol):
"""An abstract base class that represents a graph query engine on top of a underlying graph database.
This interface defines the basic methods for graph rag.
This interface defines the basic methods for graph-based RAG.
"""

def init_db(self, input_doc: List[Document] | None = None):
Expand Down
10 changes: 6 additions & 4 deletions autogen/agentchat/contrib/graph_rag/graph_rag_capability.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@

class GraphRagCapability(AgentCapability):
"""
A graph rag capability uses a graph query engine to give a conversable agent the graph rag ability.
A graph-based RAG capability uses a graph query engine to give a conversable agent the graph-based RAG ability.
An agent class with graph rag capability could
An agent class with graph-based RAG capability could
1. create a graph in the underlying database with input documents.
2. retrieved relevant information based on messages received by the agent.
3. generate answers from retrieved information and send messages back.
Expand Down Expand Up @@ -55,8 +55,10 @@ class GraphRagCapability(AgentCapability):

def __init__(self, query_engine: GraphQueryEngine):
"""
initialize graph rag capability with a graph query engine
Initialize graph-based RAG capability with a graph query engine
"""
...

def add_to_agent(self, agent: ConversableAgent): ...
def add_to_agent(self, agent: ConversableAgent):
"""Add the capability to an agent"""
...
22 changes: 11 additions & 11 deletions autogen/agentchat/contrib/swarm_agent.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright (c) 2023 - 2024, Owners of https://github.com/ag2ai
#
# SPDX-License-Identifier: Apache-2.0
import copy
import json
from dataclasses import dataclass
from enum import Enum
Expand Down Expand Up @@ -441,16 +442,19 @@ def generate_swarm_tool_reply(
message = messages[-1]
if "tool_calls" in message:

tool_calls = len(message["tool_calls"])
tool_call_count = len(message["tool_calls"])

# Loop through tool calls individually (so context can be updated after each function call)
next_agent = None
tool_responses_inner = []
contents = []
for index in range(tool_calls):
for index in range(tool_call_count):

# Deep copy to ensure no changes to messages when we insert the context variables
message_copy = copy.deepcopy(message)

# 1. add context_variables to the tool call arguments
tool_call = message["tool_calls"][index]
tool_call = message_copy["tool_calls"][index]

if tool_call["type"] == "function":
function_name = tool_call["function"]["name"]
Expand All @@ -459,20 +463,16 @@ def generate_swarm_tool_reply(
if function_name in self._function_map:
func = self._function_map[function_name] # Get the original function

# Check if function has context_variables parameter
# Inject the context variables into the tool call if it has the parameter
sig = signature(func)
if __CONTEXT_VARIABLES_PARAM_NAME__ in sig.parameters:

current_args = json.loads(tool_call["function"]["arguments"])
current_args[__CONTEXT_VARIABLES_PARAM_NAME__] = self._context_variables
# Update the tool call with new arguments
tool_call["function"]["arguments"] = json.dumps(current_args)

# Copy the message
message_copy = message.copy()
tool_calls_copy = message_copy["tool_calls"]

# remove all the tool calls except the one at the index
message_copy["tool_calls"] = [tool_calls_copy[index]]
# Ensure we are only executing the one tool at a time
message_copy["tool_calls"] = [tool_call]

# 2. generate tool calls reply
_, tool_message = self.generate_tool_calls_reply([message_copy])
Expand Down
Loading

0 comments on commit 8fddf90

Please sign in to comment.