langroid 0.30.1__tar.gz → 0.31.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {langroid-0.30.1 → langroid-0.31.0}/PKG-INFO +9 -1
- {langroid-0.30.1 → langroid-0.31.0}/README.md +8 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/embedding_models/base.py +4 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/embedding_models/models.py +105 -11
- langroid-0.31.0/langroid/experimental/team-save.py +391 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/language_models/azure_openai.py +5 -66
- {langroid-0.30.1 → langroid-0.31.0}/langroid/language_models/openai_gpt.py +0 -5
- {langroid-0.30.1 → langroid-0.31.0}/pyproject.toml +6 -6
- {langroid-0.30.1 → langroid-0.31.0}/LICENSE +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/.chainlit/config.toml +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/.chainlit/translations/bn.json +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/.chainlit/translations/en-US.json +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/.chainlit/translations/gu.json +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/.chainlit/translations/he-IL.json +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/.chainlit/translations/hi.json +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/.chainlit/translations/kn.json +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/.chainlit/translations/ml.json +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/.chainlit/translations/mr.json +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/.chainlit/translations/ta.json +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/.chainlit/translations/te.json +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/.chainlit/translations/zh-CN.json +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/base.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/batch.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/callbacks/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/callbacks/chainlit.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/chat_agent.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/chat_document.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/helpers.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/junk +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/openai_assistant.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/arangodb/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/arangodb/arangodb_agent.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/arangodb/system_messages.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/arangodb/tools.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/arangodb/utils.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/doc_chat_agent.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/lance_doc_chat_agent.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/lance_rag/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/lance_rag/critic_agent.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/lance_rag/lance_rag_task.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/lance_rag/query_planner_agent.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/lance_tools.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/neo4j/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/neo4j/csv_kg_chat.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/neo4j/neo4j_chat_agent.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/neo4j/system_messages.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/neo4j/tools.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/relevance_extractor_agent.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/retriever_agent.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/sql/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/sql/sql_chat_agent.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/sql/utils/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/sql/utils/description_extractors.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/sql/utils/populate_metadata.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/sql/utils/system_message.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/sql/utils/tools.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/special/table_chat_agent.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/structured_message.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/task.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/tool_message.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/tools/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/tools/duckduckgo_search_tool.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/tools/file_tools.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/tools/google_search_tool.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/tools/metaphor_search_tool.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/tools/orchestration.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/tools/recipient_tool.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/tools/retrieval_tool.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/tools/rewind_tool.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/tools/segment_extract_tool.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/typed_task.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent/xml_tool_message.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/agent_config.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/cachedb/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/cachedb/base.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/cachedb/momento_cachedb.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/cachedb/redis_cachedb.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/embedding_models/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/embedding_models/clustering.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/embedding_models/protoc/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/embedding_models/protoc/embeddings.proto +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/embedding_models/protoc/embeddings_pb2.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/embedding_models/protoc/embeddings_pb2.pyi +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/embedding_models/protoc/embeddings_pb2_grpc.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/embedding_models/remote_embeds.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/exceptions.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/language_models/.chainlit/config.toml +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/language_models/.chainlit/translations/en-US.json +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/language_models/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/language_models/base.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/language_models/config.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/language_models/mock_lm.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/language_models/prompt_formatter/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/language_models/prompt_formatter/base.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/language_models/prompt_formatter/hf_formatter.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/language_models/prompt_formatter/llama2_formatter.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/language_models/utils.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/mytypes.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/agent_chats.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/code-parsing.md +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/code_parser.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/config.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/document_parser.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/image_text.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/para_sentence_split.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/parse_json.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/parser.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/repo_loader.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/routing.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/search.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/spider.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/table_loader.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/url_loader.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/url_loader_cookies.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/urls.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/utils.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/parsing/web_search.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/prompts/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/prompts/chat-gpt4-system-prompt.md +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/prompts/dialog.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/prompts/prompts_config.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/prompts/templates.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/py.typed +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/pydantic_v1/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/pydantic_v1/main.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/.chainlit/config.toml +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/.chainlit/translations/en-US.json +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/algorithms/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/algorithms/graph.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/configuration.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/constants.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/docker.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/git_utils.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/globals.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/llms/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/llms/strings.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/logging.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/object_registry.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/output/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/output/citations.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/output/printing.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/output/status.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/pandas_utils.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/pydantic_utils.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/system.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/types.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/web/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/utils/web/login.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/vector_store/__init__.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/vector_store/base.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/vector_store/chromadb.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/vector_store/lancedb.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/vector_store/meilisearch.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/vector_store/momento.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/vector_store/qdrant_cloud.py +0 -0
- {langroid-0.30.1 → langroid-0.31.0}/langroid/vector_store/qdrantdb.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: langroid
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.31.0
|
4
4
|
Summary: Harness LLMs with Multi-Agent Programming
|
5
5
|
License: MIT
|
6
6
|
Author: Prasad Chalasani
|
@@ -249,6 +249,14 @@ teacher_task.run()
|
|
249
249
|
<details>
|
250
250
|
<summary> <b>Click to expand</b></summary>
|
251
251
|
|
252
|
+
- **Dec 2024:**
|
253
|
+
- [0.30.0](https://github.com/langroid/langroid/releases/tag/0.30.0) Llama-cpp embeddings.
|
254
|
+
- [0.29.0](https://github.com/langroid/langroid/releases/tag/0.29.0) Custom Azure OpenAI Client
|
255
|
+
- [0.28.0](https://github.com/langroid/langroid/releases/tag/0.28.0) `ToolMessage`: `_handler` field to override
|
256
|
+
default handler method name in `request` field.
|
257
|
+
- [0.27.0](https://github.com/langroid/langroid/releases/tag/0.27.0) OpenRouter Support.
|
258
|
+
- [0.26.0](https://github.com/langroid/langroid/releases/tag/0.26.0) Update to latest Chainlit.
|
259
|
+
- [0.25.0](https://github.com/langroid/langroid/releases/tag/0.25.0) True Async Methods for agent and user-response.
|
252
260
|
- **Nov 2024:**
|
253
261
|
- **[0.24.0](https://langroid.github.io/langroid/notes/structured-output/)**:
|
254
262
|
Enables support for `Agent`s with strict JSON schema output format on compatible LLMs and strict mode for the OpenAI tools API.
|
@@ -135,6 +135,14 @@ teacher_task.run()
|
|
135
135
|
<details>
|
136
136
|
<summary> <b>Click to expand</b></summary>
|
137
137
|
|
138
|
+
- **Dec 2024:**
|
139
|
+
- [0.30.0](https://github.com/langroid/langroid/releases/tag/0.30.0) Llama-cpp embeddings.
|
140
|
+
- [0.29.0](https://github.com/langroid/langroid/releases/tag/0.29.0) Custom Azure OpenAI Client
|
141
|
+
- [0.28.0](https://github.com/langroid/langroid/releases/tag/0.28.0) `ToolMessage`: `_handler` field to override
|
142
|
+
default handler method name in `request` field.
|
143
|
+
- [0.27.0](https://github.com/langroid/langroid/releases/tag/0.27.0) OpenRouter Support.
|
144
|
+
- [0.26.0](https://github.com/langroid/langroid/releases/tag/0.26.0) Update to latest Chainlit.
|
145
|
+
- [0.25.0](https://github.com/langroid/langroid/releases/tag/0.25.0) True Async Methods for agent and user-response.
|
138
146
|
- **Nov 2024:**
|
139
147
|
- **[0.24.0](https://langroid.github.io/langroid/notes/structured-output/)**:
|
140
148
|
Enables support for `Agent`s with strict JSON schema output format on compatible LLMs and strict mode for the OpenAI tools API.
|
@@ -24,6 +24,8 @@ class EmbeddingModel(ABC):
|
|
24
24
|
@classmethod
|
25
25
|
def create(cls, config: EmbeddingModelsConfig) -> "EmbeddingModel":
|
26
26
|
from langroid.embedding_models.models import (
|
27
|
+
AzureOpenAIEmbeddings,
|
28
|
+
AzureOpenAIEmbeddingsConfig,
|
27
29
|
FastEmbedEmbeddings,
|
28
30
|
FastEmbedEmbeddingsConfig,
|
29
31
|
LlamaCppServerEmbeddings,
|
@@ -42,6 +44,8 @@ class EmbeddingModel(ABC):
|
|
42
44
|
return RemoteEmbeddings(config)
|
43
45
|
elif isinstance(config, OpenAIEmbeddingsConfig):
|
44
46
|
return OpenAIEmbeddings(config)
|
47
|
+
elif isinstance(config, AzureOpenAIEmbeddingsConfig):
|
48
|
+
return AzureOpenAIEmbeddings(config)
|
45
49
|
elif isinstance(config, SentenceTransformerEmbeddingsConfig):
|
46
50
|
return SentenceTransformerEmbeddings(config)
|
47
51
|
elif isinstance(config, FastEmbedEmbeddingsConfig):
|
@@ -1,18 +1,20 @@
|
|
1
1
|
import atexit
|
2
2
|
import os
|
3
3
|
from functools import cached_property
|
4
|
-
from typing import Any, Callable, Dict, List, Optional
|
4
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
5
5
|
|
6
6
|
import requests
|
7
7
|
import tiktoken
|
8
8
|
from dotenv import load_dotenv
|
9
|
-
from openai import OpenAI
|
9
|
+
from openai import AzureOpenAI, OpenAI
|
10
10
|
|
11
11
|
from langroid.embedding_models.base import EmbeddingModel, EmbeddingModelsConfig
|
12
12
|
from langroid.exceptions import LangroidImportError
|
13
13
|
from langroid.mytypes import Embeddings
|
14
14
|
from langroid.parsing.utils import batched
|
15
15
|
|
16
|
+
AzureADTokenProvider = Callable[[], str]
|
17
|
+
|
16
18
|
|
17
19
|
class OpenAIEmbeddingsConfig(EmbeddingModelsConfig):
|
18
20
|
model_type: str = "openai"
|
@@ -24,6 +26,26 @@ class OpenAIEmbeddingsConfig(EmbeddingModelsConfig):
|
|
24
26
|
context_length: int = 8192
|
25
27
|
|
26
28
|
|
29
|
+
class AzureOpenAIEmbeddingsConfig(EmbeddingModelsConfig):
|
30
|
+
model_type: str = "azure-openai"
|
31
|
+
model_name: str = "text-embedding-ada-002"
|
32
|
+
api_key: str = ""
|
33
|
+
api_base: str = ""
|
34
|
+
deployment_name: Optional[str] = None
|
35
|
+
# api_version defaulted to 2024-06-01 as per https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/embeddings?tabs=python-new
|
36
|
+
# change this to required supported version
|
37
|
+
api_version: Optional[str] = "2024-06-01"
|
38
|
+
# TODO: Add auth support for Azure OpenAI via AzureADTokenProvider
|
39
|
+
azure_ad_token: Optional[str] = None
|
40
|
+
azure_ad_token_provider: Optional[AzureADTokenProvider] = None
|
41
|
+
dims: int = 1536
|
42
|
+
context_length: int = 8192
|
43
|
+
|
44
|
+
class Config:
|
45
|
+
# enable auto-loading of env vars with AZURE_OPENAI_ prefix
|
46
|
+
env_prefix = "AZURE_OPENAI_"
|
47
|
+
|
48
|
+
|
27
49
|
class SentenceTransformerEmbeddingsConfig(EmbeddingModelsConfig):
|
28
50
|
model_type: str = "sentence-transformer"
|
29
51
|
model_name: str = "BAAI/bge-large-en-v1.5"
|
@@ -58,11 +80,11 @@ class LlamaCppServerEmbeddingsConfig(EmbeddingModelsConfig):
|
|
58
80
|
class EmbeddingFunctionCallable:
|
59
81
|
"""
|
60
82
|
A callable class designed to generate embeddings for a list of texts using
|
61
|
-
the OpenAI API, with automatic retries on failure.
|
83
|
+
the OpenAI or Azure OpenAI API, with automatic retries on failure.
|
62
84
|
|
63
85
|
Attributes:
|
64
86
|
embed_model (EmbeddingModel): An instance of EmbeddingModel that provides
|
65
|
-
|
87
|
+
configuration and utilities for generating embeddings.
|
66
88
|
|
67
89
|
Methods:
|
68
90
|
__call__(input: List[str]) -> Embeddings: Generate embeddings for
|
@@ -74,8 +96,9 @@ class EmbeddingFunctionCallable:
|
|
74
96
|
Initialize the EmbeddingFunctionCallable with a specific model.
|
75
97
|
|
76
98
|
Args:
|
77
|
-
model (OpenAIEmbeddings): An instance of
|
78
|
-
|
99
|
+
model ( OpenAIEmbeddings or AzureOpenAIEmbeddings): An instance of
|
100
|
+
OpenAIEmbeddings or AzureOpenAIEmbeddings to use for
|
101
|
+
generating embeddings.
|
79
102
|
batch_size (int): Batch size
|
80
103
|
"""
|
81
104
|
self.embed_model = embed_model
|
@@ -99,8 +122,7 @@ class EmbeddingFunctionCallable:
|
|
99
122
|
Embeddings: A list of embedding vectors corresponding to the input texts.
|
100
123
|
"""
|
101
124
|
embeds = []
|
102
|
-
|
103
|
-
if isinstance(self.embed_model, OpenAIEmbeddings):
|
125
|
+
if isinstance(self.embed_model, (OpenAIEmbeddings, AzureOpenAIEmbeddings)):
|
104
126
|
tokenized_texts = self.embed_model.truncate_texts(input)
|
105
127
|
|
106
128
|
for batch in batched(tokenized_texts, self.batch_size):
|
@@ -178,6 +200,72 @@ class OpenAIEmbeddings(EmbeddingModel):
|
|
178
200
|
return self.config.dims
|
179
201
|
|
180
202
|
|
203
|
+
class AzureOpenAIEmbeddings(EmbeddingModel):
|
204
|
+
"""
|
205
|
+
Azure OpenAI embeddings model implementation.
|
206
|
+
"""
|
207
|
+
|
208
|
+
def __init__(
|
209
|
+
self, config: AzureOpenAIEmbeddingsConfig = AzureOpenAIEmbeddingsConfig()
|
210
|
+
):
|
211
|
+
"""
|
212
|
+
Initializes Azure OpenAI embeddings model.
|
213
|
+
|
214
|
+
Args:
|
215
|
+
config: Configuration for Azure OpenAI embeddings model.
|
216
|
+
Raises:
|
217
|
+
ValueError: If required Azure config values are not set.
|
218
|
+
"""
|
219
|
+
super().__init__()
|
220
|
+
self.config = config
|
221
|
+
load_dotenv()
|
222
|
+
|
223
|
+
if self.config.api_key == "":
|
224
|
+
raise ValueError(
|
225
|
+
"""AZURE_OPENAI_API_KEY env variable must be set to use
|
226
|
+
AzureOpenAIEmbeddings. Please set the AZURE_OPENAI_API_KEY value
|
227
|
+
in your .env file."""
|
228
|
+
)
|
229
|
+
|
230
|
+
if self.config.api_base == "":
|
231
|
+
raise ValueError(
|
232
|
+
"""AZURE_OPENAI_API_BASE env variable must be set to use
|
233
|
+
AzureOpenAIEmbeddings. Please set the AZURE_OPENAI_API_BASE value
|
234
|
+
in your .env file."""
|
235
|
+
)
|
236
|
+
self.client = AzureOpenAI(
|
237
|
+
api_key=self.config.api_key,
|
238
|
+
api_version=self.config.api_version,
|
239
|
+
azure_endpoint=self.config.api_base,
|
240
|
+
azure_deployment=self.config.deployment_name,
|
241
|
+
)
|
242
|
+
self.tokenizer = tiktoken.encoding_for_model(self.config.model_name)
|
243
|
+
|
244
|
+
def truncate_texts(self, texts: List[str]) -> List[List[int]]:
|
245
|
+
"""
|
246
|
+
Truncate texts to the embedding model's context length.
|
247
|
+
TODO: Maybe we should show warning, and consider doing T5 summarization?
|
248
|
+
"""
|
249
|
+
return [
|
250
|
+
self.tokenizer.encode(text, disallowed_special=())[
|
251
|
+
: self.config.context_length
|
252
|
+
]
|
253
|
+
for text in texts
|
254
|
+
]
|
255
|
+
|
256
|
+
def embedding_fn(self) -> Callable[[List[str]], Embeddings]:
|
257
|
+
"""Get the embedding function for Azure OpenAI.
|
258
|
+
|
259
|
+
Returns:
|
260
|
+
Callable that generates embeddings for input texts.
|
261
|
+
"""
|
262
|
+
return EmbeddingFunctionCallable(self, self.config.batch_size)
|
263
|
+
|
264
|
+
@property
|
265
|
+
def embedding_dims(self) -> int:
|
266
|
+
return self.config.dims
|
267
|
+
|
268
|
+
|
181
269
|
STEC = SentenceTransformerEmbeddingsConfig
|
182
270
|
|
183
271
|
|
@@ -352,13 +440,19 @@ class LlamaCppServerEmbeddings(EmbeddingModel):
|
|
352
440
|
def embedding_model(embedding_fn_type: str = "openai") -> EmbeddingModel:
|
353
441
|
"""
|
354
442
|
Args:
|
355
|
-
embedding_fn_type:
|
356
|
-
|
443
|
+
embedding_fn_type: Type of embedding model to use. Options are:
|
444
|
+
- "openai",
|
445
|
+
- "azure-openai",
|
446
|
+
- "sentencetransformer", or
|
447
|
+
- "fastembed".
|
448
|
+
(others may be added in the future)
|
357
449
|
Returns:
|
358
|
-
EmbeddingModel
|
450
|
+
EmbeddingModel: The corresponding embedding model class.
|
359
451
|
"""
|
360
452
|
if embedding_fn_type == "openai":
|
361
453
|
return OpenAIEmbeddings # type: ignore
|
454
|
+
elif embedding_fn_type == "azure-openai":
|
455
|
+
return AzureOpenAIEmbeddings # type: ignore
|
362
456
|
elif embedding_fn_type == "fastembed":
|
363
457
|
return FastEmbedEmbeddings # type: ignore
|
364
458
|
elif embedding_fn_type == "llamacppserver":
|
@@ -0,0 +1,391 @@
|
|
1
|
+
import logging
|
2
|
+
from abc import ABC, abstractmethod
|
3
|
+
from typing import Callable, Dict, List, Optional, Union
|
4
|
+
|
5
|
+
import langroid as lr
|
6
|
+
from langroid.language_models.mock_lm import MockLMConfig
|
7
|
+
|
8
|
+
# Fix logging level type
|
9
|
+
logging.basicConfig(level=logging.WARNING)
|
10
|
+
logger = logging.getLogger(__name__)
|
11
|
+
|
12
|
+
|
13
|
+
def sum_fn(s: str) -> str:
|
14
|
+
"""Dummy response for MockLM"""
|
15
|
+
nums = [
|
16
|
+
int(subpart)
|
17
|
+
for part in s.split()
|
18
|
+
for subpart in part.split(",")
|
19
|
+
if subpart.isdigit()
|
20
|
+
]
|
21
|
+
return str(sum(nums) + 1)
|
22
|
+
|
23
|
+
|
24
|
+
def user_message(msg: Union[str, lr.ChatDocument]) -> lr.ChatDocument:
|
25
|
+
"""Create a user-role msg from a string or ChatDocument"""
|
26
|
+
if isinstance(msg, lr.ChatDocument):
|
27
|
+
return msg
|
28
|
+
return lr.ChatDocument(
|
29
|
+
content=msg,
|
30
|
+
metadata=lr.ChatDocMetaData(
|
31
|
+
sender=lr.Entity.USER,
|
32
|
+
sender_name="user",
|
33
|
+
),
|
34
|
+
)
|
35
|
+
|
36
|
+
|
37
|
+
class InputContext:
|
38
|
+
"""Context for a Component to respond to"""
|
39
|
+
|
40
|
+
def __init__(self) -> None:
|
41
|
+
self.messages: List[lr.ChatDocument] = []
|
42
|
+
|
43
|
+
def add(
|
44
|
+
self, results: Union[str, List[str], lr.ChatDocument, List[lr.ChatDocument]]
|
45
|
+
) -> None:
|
46
|
+
"""
|
47
|
+
Add messages to the input messages list
|
48
|
+
"""
|
49
|
+
msgs: List[lr.ChatDocument] = []
|
50
|
+
if isinstance(results, str):
|
51
|
+
msgs = [user_message(results)]
|
52
|
+
elif isinstance(results, lr.ChatDocument):
|
53
|
+
msgs = [results]
|
54
|
+
elif isinstance(results, list):
|
55
|
+
if len(results) == 0:
|
56
|
+
return
|
57
|
+
if isinstance(results[0], str):
|
58
|
+
msgs = [user_message(r) for r in results]
|
59
|
+
else:
|
60
|
+
msgs = [r for r in results if isinstance(r, lr.ChatDocument)]
|
61
|
+
self.messages.extend(msgs)
|
62
|
+
|
63
|
+
def clear(self) -> None:
|
64
|
+
self.messages.clear()
|
65
|
+
|
66
|
+
def get_context(self) -> lr.ChatDocument:
|
67
|
+
"""Construct a user-role ChatDocument from the input messages"""
|
68
|
+
if len(self.messages) == 0:
|
69
|
+
return lr.ChatDocument(content="", metadata={"sender": lr.Entity.USER})
|
70
|
+
content = "\n".join(
|
71
|
+
f"{msg.metadata.sender_name}: {msg.content}" for msg in self.messages
|
72
|
+
)
|
73
|
+
return lr.ChatDocument(content=content, metadata={"sender": lr.Entity.USER})
|
74
|
+
|
75
|
+
|
76
|
+
class Scheduler(ABC):
|
77
|
+
"""Schedule the Components of a Team"""
|
78
|
+
|
79
|
+
def __init__(self) -> None:
|
80
|
+
self.init_state()
|
81
|
+
|
82
|
+
def init_state(self) -> None:
|
83
|
+
self.stepped = False
|
84
|
+
self.responders: List[str] = []
|
85
|
+
self.responder_counts: Dict[str, int] = {}
|
86
|
+
self.current_result: List[lr.ChatDocument] = []
|
87
|
+
|
88
|
+
@abstractmethod
|
89
|
+
def step(self) -> None:
|
90
|
+
pass
|
91
|
+
|
92
|
+
@abstractmethod
|
93
|
+
def done(self) -> bool:
|
94
|
+
pass
|
95
|
+
|
96
|
+
@abstractmethod
|
97
|
+
def result(self) -> List[lr.ChatDocument]:
|
98
|
+
pass
|
99
|
+
|
100
|
+
def run(self) -> List[lr.ChatDocument]:
|
101
|
+
self.init_state()
|
102
|
+
while not self.done():
|
103
|
+
self.step()
|
104
|
+
return self.result()
|
105
|
+
|
106
|
+
|
107
|
+
class Component(ABC):
|
108
|
+
"""A component of a Team"""
|
109
|
+
|
110
|
+
def __init__(self) -> None:
|
111
|
+
self.input = InputContext()
|
112
|
+
self._listeners: List["Component"] = []
|
113
|
+
self.name: str = ""
|
114
|
+
|
115
|
+
@abstractmethod
|
116
|
+
def run(self) -> List[lr.ChatDocument]:
|
117
|
+
pass
|
118
|
+
|
119
|
+
def listen(self, component: Union["Component", List["Component"]]) -> None:
|
120
|
+
if isinstance(component, list):
|
121
|
+
for comp in component:
|
122
|
+
comp.listeners.append(self)
|
123
|
+
else:
|
124
|
+
component.listeners.append(self)
|
125
|
+
|
126
|
+
@property
|
127
|
+
def listeners(self) -> List["Component"]:
|
128
|
+
return self._listeners
|
129
|
+
|
130
|
+
def _notify(self, results: List[lr.ChatDocument]) -> None:
|
131
|
+
logger.warning(f"{self.name} Notifying listeners...")
|
132
|
+
for listener in self.listeners:
|
133
|
+
logger.warning(f"--> Listener {listener.name} notified")
|
134
|
+
listener.input.add(results)
|
135
|
+
|
136
|
+
|
137
|
+
class SimpleScheduler(Scheduler):
|
138
|
+
def __init__(
|
139
|
+
self,
|
140
|
+
components: List[Component],
|
141
|
+
) -> None:
|
142
|
+
super().__init__()
|
143
|
+
self.components = components # Get components from team
|
144
|
+
self.stepped: bool = False
|
145
|
+
|
146
|
+
def step(self) -> None:
|
147
|
+
results = []
|
148
|
+
for comp in self.components:
|
149
|
+
result = comp.run()
|
150
|
+
if result:
|
151
|
+
results.extend(result)
|
152
|
+
self.current_result = results
|
153
|
+
self.stepped = True
|
154
|
+
|
155
|
+
def done(self) -> bool:
|
156
|
+
"""done after 1 step, i.e. all components have responded"""
|
157
|
+
return self.stepped
|
158
|
+
|
159
|
+
def result(self) -> List[lr.ChatDocument]:
|
160
|
+
return self.current_result
|
161
|
+
|
162
|
+
|
163
|
+
class OrElseScheduler(Scheduler):
|
164
|
+
"""
|
165
|
+
Implements "OrElse scheduling", i.e. if the components are A, B, C, then
|
166
|
+
in each step, it will try for a valid response from A OrElse B OrElse C,
|
167
|
+
i.e. the first component that gives a valid response is chosen.
|
168
|
+
In the next step, it will start from the next component in the list,
|
169
|
+
cycling back to the first component after the last component.
|
170
|
+
(There may be a better name than OrElseScheduler though.)
|
171
|
+
"""
|
172
|
+
|
173
|
+
def __init__(
|
174
|
+
self,
|
175
|
+
components: List[Component],
|
176
|
+
) -> None:
|
177
|
+
super().__init__()
|
178
|
+
self.components = components
|
179
|
+
self.team: Optional[Team] = None
|
180
|
+
self.current_index: int = 0
|
181
|
+
|
182
|
+
def init_state(self) -> None:
|
183
|
+
super().init_state()
|
184
|
+
self.current_index = 0
|
185
|
+
|
186
|
+
def is_valid(self, result: Optional[List[lr.ChatDocument]]) -> bool:
|
187
|
+
return result is not None and len(result) > 0
|
188
|
+
|
189
|
+
def step(self) -> None:
|
190
|
+
start_index = self.current_index
|
191
|
+
n = len(self.components)
|
192
|
+
|
193
|
+
for i in range(n):
|
194
|
+
idx = (start_index + i) % n
|
195
|
+
comp = self.components[idx]
|
196
|
+
result = comp.run()
|
197
|
+
if self.is_valid(result):
|
198
|
+
self.responders.append(comp.name)
|
199
|
+
self.responder_counts[comp.name] = (
|
200
|
+
self.responder_counts.get(comp.name, 0) + 1
|
201
|
+
)
|
202
|
+
self.current_result = result
|
203
|
+
# cycle to next component
|
204
|
+
self.current_index = (idx + 1) % n
|
205
|
+
return
|
206
|
+
|
207
|
+
def done(self) -> bool:
|
208
|
+
if self.team is None:
|
209
|
+
return False
|
210
|
+
return self.team.done(self)
|
211
|
+
|
212
|
+
def result(self) -> List[lr.ChatDocument]:
|
213
|
+
return self.current_result
|
214
|
+
|
215
|
+
|
216
|
+
class Team(Component):
|
217
|
+
def __init__(
|
218
|
+
self,
|
219
|
+
name: str,
|
220
|
+
done_condition: Optional[Callable[["Team", Scheduler], bool]] = None,
|
221
|
+
) -> None:
|
222
|
+
super().__init__()
|
223
|
+
self.name = name
|
224
|
+
self.components: List[Component] = []
|
225
|
+
self.scheduler: Optional[Scheduler] = None
|
226
|
+
self.done_condition = done_condition or Team.default_done_condition
|
227
|
+
|
228
|
+
def set_done_condition(
|
229
|
+
self, done_condition: Callable[["Team", Scheduler], bool]
|
230
|
+
) -> None:
|
231
|
+
self.done_condition = done_condition
|
232
|
+
|
233
|
+
def done(self, scheduler: Scheduler) -> bool:
|
234
|
+
return self.done_condition(self, scheduler)
|
235
|
+
|
236
|
+
def default_done_condition(self, scheduler: Scheduler) -> bool:
|
237
|
+
# Default condition, can be overridden
|
238
|
+
return False
|
239
|
+
|
240
|
+
def add_scheduler(self, scheduler_class: type) -> None:
|
241
|
+
self.scheduler = scheduler_class(self.components)
|
242
|
+
if hasattr(self.scheduler, "team"):
|
243
|
+
setattr(self.scheduler, "team", self)
|
244
|
+
|
245
|
+
def add(self, component: Union[Component, List[Component]]) -> None:
|
246
|
+
if isinstance(component, list):
|
247
|
+
self.components.extend(component)
|
248
|
+
else:
|
249
|
+
self.components.append(component)
|
250
|
+
|
251
|
+
def reset(self) -> None:
|
252
|
+
self.input.clear()
|
253
|
+
if self.scheduler is not None:
|
254
|
+
self.scheduler.init_state()
|
255
|
+
|
256
|
+
def run(self, input: str | lr.ChatDocument | None = None) -> List[lr.ChatDocument]:
|
257
|
+
if input is not None:
|
258
|
+
self.input.add(input)
|
259
|
+
if self.scheduler is None:
|
260
|
+
raise ValueError(
|
261
|
+
f"Team '{self.name}' has no scheduler. Call add_scheduler() first."
|
262
|
+
)
|
263
|
+
input_str = self.input.get_context().content
|
264
|
+
logger.warning(f"Running team {self.name}... on input = {input_str}")
|
265
|
+
# push the input of self to each component that's a listener of self.
|
266
|
+
n_pushed = 0
|
267
|
+
for comp in self.components:
|
268
|
+
|
269
|
+
if comp in self.listeners:
|
270
|
+
comp.input.add(self.input.messages)
|
271
|
+
n_pushed += 1
|
272
|
+
if len(self.input.messages) > 0 and n_pushed == 0:
|
273
|
+
logger.warning(
|
274
|
+
"""
|
275
|
+
Warning: Team inputs not pushed to any components!
|
276
|
+
You may not be able to run any components unless they have their
|
277
|
+
own inputs. Make sure to set up component to listen to parent team
|
278
|
+
if needed.
|
279
|
+
"""
|
280
|
+
)
|
281
|
+
# clear own input since we've pushed it to internal components
|
282
|
+
self.input.clear()
|
283
|
+
|
284
|
+
result = self.scheduler.run()
|
285
|
+
if len(result) > 0:
|
286
|
+
self._notify(result)
|
287
|
+
result_value = result[0].content if len(result) > 0 else "null"
|
288
|
+
logger.warning(f"Team {self.name} done: {result_value}")
|
289
|
+
return result
|
290
|
+
|
291
|
+
|
292
|
+
class DummyAgent:
|
293
|
+
def __init__(self, name: str) -> None:
|
294
|
+
self.name = name
|
295
|
+
|
296
|
+
def process(self, data: str) -> str:
|
297
|
+
return f"{self.name} processed: {data}"
|
298
|
+
|
299
|
+
|
300
|
+
class TaskComponent(Component):
|
301
|
+
def __init__(self, task: lr.Task) -> None:
|
302
|
+
super().__init__()
|
303
|
+
self.task = task
|
304
|
+
self.name = task.agent.config.name
|
305
|
+
|
306
|
+
def run(self, input: str | lr.ChatDocument | None = None) -> List[lr.ChatDocument]:
|
307
|
+
if input is not None:
|
308
|
+
self.input.add(input)
|
309
|
+
input_msg = self.input.get_context()
|
310
|
+
if input_msg.content == "":
|
311
|
+
return []
|
312
|
+
logger.warning(f"Running task {self.name} on input = {input_msg.content}")
|
313
|
+
result = self.task.run(input_msg)
|
314
|
+
result_value = result.content if result else "null"
|
315
|
+
logger.warning(f"Task {self.name} done: {result_value}")
|
316
|
+
result_list = [result] if result else []
|
317
|
+
if len(result_list) > 0:
|
318
|
+
self._notify(result_list)
|
319
|
+
self.input.clear() # clear own input since we just consumed it!
|
320
|
+
return result_list
|
321
|
+
|
322
|
+
|
323
|
+
def make_task(name: str, sys: str = "") -> TaskComponent:
|
324
|
+
llm_config = MockLMConfig(response_fn=sum_fn)
|
325
|
+
agent = lr.ChatAgent(
|
326
|
+
lr.ChatAgentConfig(
|
327
|
+
llm=llm_config,
|
328
|
+
name=name,
|
329
|
+
)
|
330
|
+
)
|
331
|
+
# set as single_round since there are no Tools
|
332
|
+
task = lr.Task(agent, interactive=False, single_round=True)
|
333
|
+
return TaskComponent(task)
|
334
|
+
|
335
|
+
|
336
|
+
if __name__ == "__main__":
|
337
|
+
# Create agents, tasks
|
338
|
+
t1 = make_task("a1")
|
339
|
+
t2 = make_task("a2")
|
340
|
+
t3 = make_task("a3")
|
341
|
+
|
342
|
+
# done conditions for each time
|
343
|
+
def team1_done_condition(team: Team, scheduler: Scheduler) -> bool:
|
344
|
+
return (
|
345
|
+
scheduler.responder_counts.get("a1", 0) >= 2
|
346
|
+
and scheduler.responder_counts.get("a2", 0) >= 2
|
347
|
+
)
|
348
|
+
|
349
|
+
def team2_done_condition(team: Team, scheduler: Scheduler) -> bool:
|
350
|
+
return "a3" in scheduler.responders
|
351
|
+
|
352
|
+
def general_team_done_condition(team: Team, scheduler: Scheduler) -> bool:
|
353
|
+
# Example: all components have responded at least once
|
354
|
+
return len(set(scheduler.responders)) == len(team.components)
|
355
|
+
|
356
|
+
# Create teams
|
357
|
+
team1 = Team("T1", done_condition=team1_done_condition)
|
358
|
+
team2 = Team("T2", done_condition=team2_done_condition)
|
359
|
+
|
360
|
+
team = Team("Team", done_condition=general_team_done_condition)
|
361
|
+
|
362
|
+
team1.add_scheduler(OrElseScheduler)
|
363
|
+
team2.add_scheduler(OrElseScheduler)
|
364
|
+
team.add_scheduler(OrElseScheduler)
|
365
|
+
|
366
|
+
team.add([team1, team2])
|
367
|
+
|
368
|
+
# Build hierarchy
|
369
|
+
team1.add([t1, t2])
|
370
|
+
team2.add(t3)
|
371
|
+
|
372
|
+
# Set up listening
|
373
|
+
# team2.listen(team1) # listens to team1 final result
|
374
|
+
team1.listen(team)
|
375
|
+
t1.listen(team1)
|
376
|
+
t2.listen(t1)
|
377
|
+
t1.listen(t2)
|
378
|
+
# TODO should we forbid listening to a component OUTSIDE the team?
|
379
|
+
|
380
|
+
# t3 listens to its parent team2 =>
|
381
|
+
# any input to team2 gets pushed to t3 when t3 runs
|
382
|
+
team2.listen([t1, t2])
|
383
|
+
t3.listen(team2)
|
384
|
+
|
385
|
+
# TODO - we should either define which component of a team gets the teams inputs,
|
386
|
+
# or explicitly add messages to a specific component of the team
|
387
|
+
|
388
|
+
print("Running top-level team...")
|
389
|
+
result = team.run("1")
|
390
|
+
|
391
|
+
##########
|