modaic 0.1.0__py3-none-any.whl
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.
Potentially problematic release.
This version of modaic might be problematic. Click here for more details.
- modaic/__init__.py +25 -0
- modaic/agents/rag_agent.py +33 -0
- modaic/agents/registry.py +84 -0
- modaic/auto_agent.py +228 -0
- modaic/context/__init__.py +34 -0
- modaic/context/base.py +1064 -0
- modaic/context/dtype_mapping.py +25 -0
- modaic/context/table.py +585 -0
- modaic/context/text.py +94 -0
- modaic/databases/__init__.py +35 -0
- modaic/databases/graph_database.py +269 -0
- modaic/databases/sql_database.py +355 -0
- modaic/databases/vector_database/__init__.py +12 -0
- modaic/databases/vector_database/benchmarks/baseline.py +123 -0
- modaic/databases/vector_database/benchmarks/common.py +48 -0
- modaic/databases/vector_database/benchmarks/fork.py +132 -0
- modaic/databases/vector_database/benchmarks/threaded.py +119 -0
- modaic/databases/vector_database/vector_database.py +722 -0
- modaic/databases/vector_database/vendors/milvus.py +408 -0
- modaic/databases/vector_database/vendors/mongodb.py +0 -0
- modaic/databases/vector_database/vendors/pinecone.py +0 -0
- modaic/databases/vector_database/vendors/qdrant.py +1 -0
- modaic/exceptions.py +38 -0
- modaic/hub.py +305 -0
- modaic/indexing.py +127 -0
- modaic/module_utils.py +341 -0
- modaic/observability.py +275 -0
- modaic/precompiled.py +429 -0
- modaic/query_language.py +321 -0
- modaic/storage/__init__.py +3 -0
- modaic/storage/file_store.py +239 -0
- modaic/storage/pickle_store.py +25 -0
- modaic/types.py +287 -0
- modaic/utils.py +21 -0
- modaic-0.1.0.dist-info/METADATA +281 -0
- modaic-0.1.0.dist-info/RECORD +39 -0
- modaic-0.1.0.dist-info/WHEEL +5 -0
- modaic-0.1.0.dist-info/licenses/LICENSE +31 -0
- modaic-0.1.0.dist-info/top_level.txt +1 -0
modaic/types.py
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
from typing import Any, List, Literal, Optional, Tuple, Type, get_origin
|
|
2
|
+
|
|
3
|
+
from pydantic import (
|
|
4
|
+
BaseModel,
|
|
5
|
+
RootModel,
|
|
6
|
+
WithJsonSchema,
|
|
7
|
+
field_validator,
|
|
8
|
+
model_validator,
|
|
9
|
+
)
|
|
10
|
+
from pydantic import (
|
|
11
|
+
Field as PydanticField,
|
|
12
|
+
)
|
|
13
|
+
from pydantic.fields import FieldInfo
|
|
14
|
+
from pydantic_core import PydanticUndefined
|
|
15
|
+
from typing_extensions import Annotated
|
|
16
|
+
|
|
17
|
+
from .exceptions import SchemaError
|
|
18
|
+
|
|
19
|
+
int8 = Annotated[
|
|
20
|
+
int,
|
|
21
|
+
PydanticField(ge=-128, le=127),
|
|
22
|
+
WithJsonSchema({"type": "integer", "format": "int8"}),
|
|
23
|
+
]
|
|
24
|
+
int16 = Annotated[
|
|
25
|
+
int,
|
|
26
|
+
PydanticField(ge=-32768, le=32767),
|
|
27
|
+
WithJsonSchema({"type": "integer", "format": "int16"}),
|
|
28
|
+
]
|
|
29
|
+
int32 = Annotated[
|
|
30
|
+
int,
|
|
31
|
+
PydanticField(ge=-(2**31), le=2**31 - 1),
|
|
32
|
+
WithJsonSchema({"type": "integer", "format": "int32"}),
|
|
33
|
+
]
|
|
34
|
+
int64 = Annotated[
|
|
35
|
+
int,
|
|
36
|
+
PydanticField(ge=-(2**63), le=2**63 - 1),
|
|
37
|
+
WithJsonSchema({"type": "integer", "format": "int64"}),
|
|
38
|
+
]
|
|
39
|
+
double = Annotated[
|
|
40
|
+
float,
|
|
41
|
+
PydanticField(ge=-1.87e308, le=1.87e308),
|
|
42
|
+
WithJsonSchema({"type": "number", "format": "double"}),
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ArrayMeta(type):
|
|
47
|
+
def __getitem__(cls, params: type | tuple[type, int]):
|
|
48
|
+
if isinstance(params, tuple) and len(params) == 2:
|
|
49
|
+
dtype = params[0]
|
|
50
|
+
max_size = params[1]
|
|
51
|
+
elif isinstance(params, type) or get_origin(params) is Annotated:
|
|
52
|
+
dtype = params
|
|
53
|
+
max_size = None
|
|
54
|
+
else:
|
|
55
|
+
raise TypeError(
|
|
56
|
+
f"{cls.__name__} requires either 2 parameters: {cls.__name__}[dtype, max_size] or 1 parameter: {cls.__name__}[dtype]"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
assert isinstance(dtype, type) or get_origin(dtype) is Annotated, f"dtype must be a type, got {dtype}"
|
|
60
|
+
assert max_size is None or (isinstance(max_size, int) and max_size > 0), (
|
|
61
|
+
f"max_size must be an int or None, got {max_size}"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return Annotated[
|
|
65
|
+
List[dtype],
|
|
66
|
+
PydanticField(min_length=0, max_length=max_size),
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class Array(List, metaclass=ArrayMeta):
|
|
71
|
+
"""
|
|
72
|
+
Array field type for `Context`. Must be created with Array[dtype, max_size]
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
dtype (Type): The type of the elements in the array.
|
|
76
|
+
max_size (int): The maximum size of the array.
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
A `Email` context class that stores an email's content and recipients.
|
|
80
|
+
```python
|
|
81
|
+
from modaic.types import Array
|
|
82
|
+
from modaic.context import Context
|
|
83
|
+
|
|
84
|
+
class Email(Context):
|
|
85
|
+
content: str
|
|
86
|
+
recipients: Array[str, 100]
|
|
87
|
+
```
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class StringMeta(type):
|
|
94
|
+
def __getitem__(cls, params: int):
|
|
95
|
+
if not isinstance(params, int):
|
|
96
|
+
raise TypeError(f"{cls.__name__} requires exactly 1 parameters: {cls.__name__}[max_size]")
|
|
97
|
+
|
|
98
|
+
max_size = params
|
|
99
|
+
if not isinstance(max_size, int) or max_size <= 1:
|
|
100
|
+
raise TypeError(f"Max size must be a >= 1, got {max_size}")
|
|
101
|
+
|
|
102
|
+
return Annotated[
|
|
103
|
+
str,
|
|
104
|
+
PydanticField(max_length=max_size),
|
|
105
|
+
WithJsonSchema({"type": "string", "maxLength": max_size}),
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class String(str, metaclass=StringMeta):
|
|
110
|
+
"""String type that can be parameterized with max_length constraint.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
max_size (int): The maximum length of the string.
|
|
114
|
+
|
|
115
|
+
Example:
|
|
116
|
+
```python
|
|
117
|
+
from modaic.types import String
|
|
118
|
+
from modaic.context import Context
|
|
119
|
+
|
|
120
|
+
class Email(Context):
|
|
121
|
+
subject: String[100]
|
|
122
|
+
content: str
|
|
123
|
+
recipients: Array[str, 100]
|
|
124
|
+
```
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def fetch_type(metadata: list, type_class: Type) -> Optional[Type]:
|
|
131
|
+
return next((x for x in metadata if isinstance(x, type_class)), None)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def get_original_class(field_info: FieldInfo, default: Optional[Type] = None) -> Type:
|
|
135
|
+
if json_schema_extra := getattr(field_info, "json_schema_extra", None):
|
|
136
|
+
return json_schema_extra.get("original_class", default)
|
|
137
|
+
return default
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
int_format = Literal["int8", "int16", "int32", "int64"]
|
|
141
|
+
float_format = Literal["float", "double"]
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class SchemaField(BaseModel):
|
|
145
|
+
optional: bool
|
|
146
|
+
type: Literal["array", "integer", "number", "object", "string", "boolean"]
|
|
147
|
+
format: int_format | float_format | None
|
|
148
|
+
size: Optional[int]
|
|
149
|
+
inner_type: Optional["InnerField"]
|
|
150
|
+
is_id: bool = False
|
|
151
|
+
is_unique: bool = False
|
|
152
|
+
|
|
153
|
+
@model_validator(mode="after")
|
|
154
|
+
def validate_field(self) -> "SchemaField":
|
|
155
|
+
if self.type == "array" and self.inner_type is None:
|
|
156
|
+
raise SchemaError("Array type must have an inner type")
|
|
157
|
+
if self.is_id and not self.is_unique:
|
|
158
|
+
raise SchemaError("id field must be unique")
|
|
159
|
+
if self.is_id and self.optional:
|
|
160
|
+
raise SchemaError("id field cannot be optional")
|
|
161
|
+
if self.is_id and self.type != "string":
|
|
162
|
+
raise SchemaError("id field must be a string")
|
|
163
|
+
# NOTE: handle case where the float type was used and therefore not annotated with a format
|
|
164
|
+
if self.type == "number":
|
|
165
|
+
self.format = self.format or "float"
|
|
166
|
+
return self
|
|
167
|
+
|
|
168
|
+
@staticmethod
|
|
169
|
+
def from_json_schema_property(
|
|
170
|
+
field_schema: dict, is_id: bool = False, is_unique: Optional[bool] = None
|
|
171
|
+
) -> "SchemaField":
|
|
172
|
+
inspected_type, is_optional = _inspect_type(field_schema)
|
|
173
|
+
if "maxItems" in inspected_type and "maxLength" in inspected_type:
|
|
174
|
+
raise SchemaError("maxItems and maxLength cannot both be present in a schema field")
|
|
175
|
+
if "items" in inspected_type:
|
|
176
|
+
inner_type = InnerField.from_json_schema_property(inspected_type["items"])
|
|
177
|
+
else:
|
|
178
|
+
inner_type = None
|
|
179
|
+
if is_unique is None:
|
|
180
|
+
is_unique = is_id
|
|
181
|
+
|
|
182
|
+
return SchemaField(
|
|
183
|
+
optional=is_optional,
|
|
184
|
+
type=inspected_type["type"],
|
|
185
|
+
format=inspected_type.get("format", None),
|
|
186
|
+
size=inspected_type.get("maxItems", None) or inspected_type.get("maxLength", None),
|
|
187
|
+
inner_type=inner_type,
|
|
188
|
+
is_id=is_id,
|
|
189
|
+
is_unique=is_unique,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class InnerField(BaseModel):
|
|
194
|
+
type: Literal["integer", "number", "string", "boolean"]
|
|
195
|
+
format: int_format | float_format | None = None
|
|
196
|
+
size: Optional[int] = None
|
|
197
|
+
|
|
198
|
+
@model_validator(mode="after")
|
|
199
|
+
def validate_field(self) -> "InnerField":
|
|
200
|
+
if self.type == "number":
|
|
201
|
+
self.format = self.format or "float"
|
|
202
|
+
return self
|
|
203
|
+
|
|
204
|
+
@staticmethod
|
|
205
|
+
def from_json_schema_property(inner_schema: dict) -> "InnerField":
|
|
206
|
+
inspected_type, is_optional = _inspect_type(inner_schema)
|
|
207
|
+
# NOTE: handle case where the float type was used and therefore not annotated with a format
|
|
208
|
+
if is_optional:
|
|
209
|
+
raise SchemaError("Array/List elements cannot be None/null")
|
|
210
|
+
return InnerField(
|
|
211
|
+
type=inspected_type["type"],
|
|
212
|
+
format=inspected_type.get("format", None),
|
|
213
|
+
size=inspected_type.get("size", None),
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class Schema(RootModel[dict[str, SchemaField]]):
|
|
218
|
+
@field_validator("root")
|
|
219
|
+
@classmethod
|
|
220
|
+
def validate_is_id(cls, v: dict[str, SchemaField]) -> dict[str, SchemaField]:
|
|
221
|
+
offenders = [k for k, sf in v.items() if sf.is_id and k != "id"]
|
|
222
|
+
if offenders:
|
|
223
|
+
raise SchemaError(
|
|
224
|
+
"is_id can only be True for the key 'id'; offending keys: " + ", ".join(repr(k) for k in offenders)
|
|
225
|
+
)
|
|
226
|
+
return v
|
|
227
|
+
|
|
228
|
+
@staticmethod
|
|
229
|
+
def from_json_schema(schema: dict) -> "Schema":
|
|
230
|
+
"""
|
|
231
|
+
Converts an OpenAPI JSON schema to a Modaic schema that can be used with modaic databases.
|
|
232
|
+
Warnings:
|
|
233
|
+
Not designed to handle all edge cases of OpenAPI schemas. Only designed to work with jsons output by pydantics BaseModel model_json_schema()
|
|
234
|
+
"""
|
|
235
|
+
validated_fields = {}
|
|
236
|
+
for field_name, field_schema in schema["properties"].items():
|
|
237
|
+
schema_field = SchemaField.from_json_schema_property(field_schema, is_id=field_name == "id")
|
|
238
|
+
validated_fields[field_name] = schema_field
|
|
239
|
+
return Schema(validated_fields)
|
|
240
|
+
|
|
241
|
+
def as_dict(self) -> dict[str, SchemaField]:
|
|
242
|
+
return self.root
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _inspect_type(field_schema: dict) -> Tuple[dict, bool]:
|
|
246
|
+
"""
|
|
247
|
+
This function inspects the json schema ensuring it is a valid modaic schema. Returns from this function are guaranteed to:
|
|
248
|
+
1. Be a dictionary containing the key "type"
|
|
249
|
+
2. Not have unions other than a single union with null
|
|
250
|
+
3. All {"$ref": "..."} is replaced with {"type": "object"}
|
|
251
|
+
Returns:
|
|
252
|
+
Tuple[dict, bool]: the dict containing the type, and a boolean indicating if the field is optional
|
|
253
|
+
"""
|
|
254
|
+
if anyOf := field_schema.get("anyOf", None): # noqa: N806
|
|
255
|
+
if len(anyOf) > 2 or not any(_is_null(item) for item in anyOf):
|
|
256
|
+
raise SchemaError("Unions are not supported for Modaic Schemas")
|
|
257
|
+
elif any(not _is_null(type_ := item) for item in anyOf):
|
|
258
|
+
return _handle_if_ref(type_), True
|
|
259
|
+
else:
|
|
260
|
+
raise SchemaError("Invalid field schema")
|
|
261
|
+
elif "type" in field_schema:
|
|
262
|
+
return _handle_if_ref(field_schema), False
|
|
263
|
+
elif "$ref" in field_schema:
|
|
264
|
+
return {"type": "object"}, False
|
|
265
|
+
else:
|
|
266
|
+
raise SchemaError("Invalid field schema")
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _handle_if_ref(field_schema: dict) -> dict:
|
|
270
|
+
"""
|
|
271
|
+
Handles the case where the field is a reference to another schema. Returns an {"type": "object"}
|
|
272
|
+
"""
|
|
273
|
+
if "$ref" in field_schema:
|
|
274
|
+
return {"type": "object"}
|
|
275
|
+
else:
|
|
276
|
+
return field_schema
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _is_null(field_schema: dict) -> bool:
|
|
280
|
+
return field_schema.get("type", "") == "null"
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def Field(default: Any = PydanticUndefined, *, hidden: bool = False, **kwargs) -> FieldInfo: # noqa: N802, ANN001
|
|
284
|
+
if hidden:
|
|
285
|
+
return PydanticField(default=default, **kwargs, json_schema_extra={"hidden": True})
|
|
286
|
+
else:
|
|
287
|
+
return PydanticField(default=default, **kwargs)
|
modaic/utils.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from dotenv import load_dotenv
|
|
6
|
+
|
|
7
|
+
load_dotenv()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def compute_cache_dir() -> Path:
|
|
11
|
+
"""Return the cache directory used to stage internal modules."""
|
|
12
|
+
cache_dir_env = os.getenv("MODAIC_CACHE")
|
|
13
|
+
default_cache_dir = Path(os.path.expanduser("~")) / ".cache" / "modaic"
|
|
14
|
+
cache_dir = Path(cache_dir_env).expanduser().resolve() if cache_dir_env else default_cache_dir.resolve()
|
|
15
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
16
|
+
return cache_dir
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def validate_project_name(text: str) -> bool:
|
|
20
|
+
"""Letters, numbers, underscore, hyphen"""
|
|
21
|
+
assert bool(re.match(r'^[a-zA-Z0-9_]+$', text)), "Invalid project name. Must contain only letters, numbers, and underscore."
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: modaic
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: **Mod**ular **A**gent **I**nfrastructure **C**ollective, a python framework for managing and sharing DSPy agents
|
|
5
|
+
Author-email: Tyrin <tytodd@mit.edu>, Farouk <farouk@modaic.dev>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 Modaic Inc
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
Additional Terms:
|
|
31
|
+
|
|
32
|
+
1. You may not modify this Software in any way that changes the default hub
|
|
33
|
+
endpoint, nor distribute derivative works that route agents or models to
|
|
34
|
+
a hub other than modaic.dev.
|
|
35
|
+
|
|
36
|
+
2. All other rights are granted as per the MIT License.
|
|
37
|
+
|
|
38
|
+
Requires-Python: >=3.10
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
License-File: LICENSE
|
|
41
|
+
Requires-Dist: aenum>=3.1.16
|
|
42
|
+
Requires-Dist: dspy>=2.6.27
|
|
43
|
+
Requires-Dist: duckdb>=1.3.2
|
|
44
|
+
Requires-Dist: filetype>=1.2.0
|
|
45
|
+
Requires-Dist: gitpython>=3.1.45
|
|
46
|
+
Requires-Dist: immutables>=0.21
|
|
47
|
+
Requires-Dist: langchain-community>=0.3.29
|
|
48
|
+
Requires-Dist: langchain-core>=0.3.72
|
|
49
|
+
Requires-Dist: langchain-text-splitters>=0.3.9
|
|
50
|
+
Requires-Dist: more-itertools>=10.8.0
|
|
51
|
+
Requires-Dist: opik>=1.8.42
|
|
52
|
+
Requires-Dist: pillow>=11.3.0
|
|
53
|
+
Requires-Dist: pymilvus>=2.5.14
|
|
54
|
+
Requires-Dist: sqlalchemy>=2.0.42
|
|
55
|
+
Requires-Dist: tomlkit>=0.13.3
|
|
56
|
+
Provides-Extra: pinecone
|
|
57
|
+
Requires-Dist: pinecone>=7.3.0; extra == "pinecone"
|
|
58
|
+
Dynamic: license-file
|
|
59
|
+
|
|
60
|
+
[](https://docs.modaic.dev)
|
|
61
|
+
# Modaic 🐙
|
|
62
|
+
**Mod**ular **A**gent **I**nfrastructure **C**ollective, a Python framework for building AI agents with structured context management, database integration, and retrieval-augmented generation (RAG) capabilities.
|
|
63
|
+
|
|
64
|
+
## Overview
|
|
65
|
+
|
|
66
|
+
Modaic provides a comprehensive toolkit for creating intelligent agents that can work with diverse data sources including tables, documents, and databases. Built on top of DSPy, it offers a way to share and manage declarative agent architectures with integrated vector, SQL, and graph database support.
|
|
67
|
+
|
|
68
|
+
## Key Features
|
|
69
|
+
|
|
70
|
+
- **Hub Support**: Load and share precompiled agents from Modaic Hub
|
|
71
|
+
- **Context Management**: Structured handling of molecular and atomic context types
|
|
72
|
+
- **Database Integration**: Support for Vector (Milvus, Pinecone, Qdrant), SQL (SQLite, MySQL, PostgreSQL), and Graph (Memgraph, Neo4j)
|
|
73
|
+
- **Agent Framework**: Precompiled and auto-loading agent architectures
|
|
74
|
+
- **Table Processing**: Advanced Excel/CSV processing with SQL querying capabilities
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
## Installation
|
|
78
|
+
|
|
79
|
+
### Using uv (recommended)
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
uv add modaic
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Optional (for hub operations):
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
export MODAIC_TOKEN="<your-token>"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Using pip
|
|
92
|
+
Please note that you will not be able to push agents to the Modaic Hub with pip.
|
|
93
|
+
```bash
|
|
94
|
+
pip install modaic
|
|
95
|
+
```
|
|
96
|
+
## Quick Start
|
|
97
|
+
|
|
98
|
+
### Creating a Simple Agent
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from modaic import PrecompiledAgent, PrecompiledConfig
|
|
102
|
+
|
|
103
|
+
class WeatherConfig(PrecompiledConfig):
|
|
104
|
+
weather: str = "sunny"
|
|
105
|
+
|
|
106
|
+
class WeatherAgent(PrecompiledAgent):
|
|
107
|
+
config: WeatherConfig
|
|
108
|
+
|
|
109
|
+
def __init__(self, config: WeatherConfig, **kwargs):
|
|
110
|
+
super().__init__(config, **kwargs)
|
|
111
|
+
|
|
112
|
+
def forward(self, query: str) -> str:
|
|
113
|
+
return f"The weather in {query} is {self.config.weather}."
|
|
114
|
+
|
|
115
|
+
agent = WeatherAgent(WeatherConfig())
|
|
116
|
+
print(agent(query="Tokyo"))
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Save and load locally:
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
agent.save_precompiled("./my-weather")
|
|
123
|
+
|
|
124
|
+
from modaic import AutoAgent, AutoConfig
|
|
125
|
+
|
|
126
|
+
cfg = AutoConfig.from_precompiled("./my-weather", local=True)
|
|
127
|
+
loaded = AutoAgent.from_precompiled("./my-weather", local=True)
|
|
128
|
+
print(loaded(query="Kyoto"))
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Working with Tables
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from pathlib import Path
|
|
135
|
+
from modaic.context import Table, TableFile
|
|
136
|
+
import pandas as pd
|
|
137
|
+
|
|
138
|
+
# Load from Excel/CSV
|
|
139
|
+
excel = TableFile.from_file(
|
|
140
|
+
file_ref="employees.xlsx",
|
|
141
|
+
file=Path("employees.xlsx"),
|
|
142
|
+
file_type="xlsx",
|
|
143
|
+
)
|
|
144
|
+
csv = TableFile.from_file(
|
|
145
|
+
file_ref="data.csv",
|
|
146
|
+
file=Path("data.csv"),
|
|
147
|
+
file_type="csv",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# Create from DataFrame
|
|
151
|
+
df = pd.DataFrame({"col1": [1, 2, 3], "col2": [4, 5, 6]})
|
|
152
|
+
table = Table(df=df, name="my_table")
|
|
153
|
+
|
|
154
|
+
# Query with SQL (refer to in-memory table as `this`)
|
|
155
|
+
result = table.query("SELECT * FROM this WHERE col1 > 1")
|
|
156
|
+
|
|
157
|
+
# Convert to markdown
|
|
158
|
+
markdown = table.markdown()
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Database Integration
|
|
162
|
+
|
|
163
|
+
#### SQL Database
|
|
164
|
+
```python
|
|
165
|
+
from modaic.databases import SQLDatabase, SQLiteBackend
|
|
166
|
+
|
|
167
|
+
# Configure and connect
|
|
168
|
+
backend = SQLiteBackend(db_path="my_database.db")
|
|
169
|
+
db = SQLDatabase(backend)
|
|
170
|
+
|
|
171
|
+
# Add table
|
|
172
|
+
db.add_table(table)
|
|
173
|
+
|
|
174
|
+
# Query
|
|
175
|
+
rows = db.fetchall("SELECT * FROM my_table")
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### Vector Database
|
|
179
|
+
#### Graph Database
|
|
180
|
+
```python
|
|
181
|
+
from modaic.context import Context, Relation
|
|
182
|
+
from modaic.databases import GraphDatabase, MemgraphConfig, Neo4jConfig
|
|
183
|
+
|
|
184
|
+
# Configure backend (choose one)
|
|
185
|
+
mg = GraphDatabase(MemgraphConfig())
|
|
186
|
+
# or
|
|
187
|
+
neo = GraphDatabase(Neo4jConfig())
|
|
188
|
+
|
|
189
|
+
# Define nodes
|
|
190
|
+
class Person(Context):
|
|
191
|
+
name: str
|
|
192
|
+
age: int
|
|
193
|
+
|
|
194
|
+
class KNOWS(Relation):
|
|
195
|
+
since: int
|
|
196
|
+
|
|
197
|
+
alice = Person(name="Alice", age=30)
|
|
198
|
+
bob = Person(name="Bob", age=28)
|
|
199
|
+
|
|
200
|
+
# Save nodes
|
|
201
|
+
alice.save(mg)
|
|
202
|
+
bob.save(mg)
|
|
203
|
+
|
|
204
|
+
# Create relationship (Alice)-[KNOWS]->(Bob)
|
|
205
|
+
rel = (alice >> KNOWS(since=2020) >> bob)
|
|
206
|
+
rel.save(mg)
|
|
207
|
+
|
|
208
|
+
# Query
|
|
209
|
+
rows = mg.execute_and_fetch("MATCH (a:Person)-[r:KNOWS]->(b:Person) RETURN a, r, b LIMIT 5")
|
|
210
|
+
```
|
|
211
|
+
```python
|
|
212
|
+
from modaic import Embedder
|
|
213
|
+
from modaic.context import Text
|
|
214
|
+
from modaic.databases import VectorDatabase, MilvusBackend
|
|
215
|
+
|
|
216
|
+
# Setup embedder and backend
|
|
217
|
+
embedder = Embedder("openai/text-embedding-3-small")
|
|
218
|
+
backend = MilvusBackend.from_local("vector.db") # milvus lite
|
|
219
|
+
|
|
220
|
+
# Initialize database
|
|
221
|
+
vdb = VectorDatabase(backend=backend, embedder=embedder, payload_class=Text)
|
|
222
|
+
|
|
223
|
+
# Create collection and add records
|
|
224
|
+
vdb.create_collection("my_collection", payload_class=Text)
|
|
225
|
+
vdb.add_records("my_collection", [Text(text="hello world"), Text(text="modaic makes sharing agents easy")])
|
|
226
|
+
|
|
227
|
+
# Search
|
|
228
|
+
results = vdb.search("my_collection", query="hello", k=3)
|
|
229
|
+
top_hit_text = results[0][0].context.text
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Architecture
|
|
233
|
+
### Agent Types
|
|
234
|
+
|
|
235
|
+
1. **PrecompiledAgent**: Statically defined agents with explicit configuration
|
|
236
|
+
2. **AutoAgent**: Dynamically loaded agents from Modaic Hub or local repositories
|
|
237
|
+
|
|
238
|
+
### Database Support
|
|
239
|
+
|
|
240
|
+
| Database Type | Providers | Use Case |
|
|
241
|
+
|---------------|-----------|----------|
|
|
242
|
+
| **Vector** | Milvus | Semantic search, RAG |
|
|
243
|
+
| **SQL** | SQLite, MySQL, PostgreSQL | Structured queries, table storage |
|
|
244
|
+
|
|
245
|
+
## Examples
|
|
246
|
+
|
|
247
|
+
### TableRAG Example
|
|
248
|
+
|
|
249
|
+
The TableRAG example demonstrates a complete RAG pipeline for table-based question answering:
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
from modaic.precompiled_agent import PrecompiledConfig, PrecompiledAgent
|
|
253
|
+
from modaic.context import Table
|
|
254
|
+
from modaic.databases import VectorDatabase, SQLDatabase
|
|
255
|
+
from modaic.types import Indexer
|
|
256
|
+
|
|
257
|
+
class TableRAGConfig(PrecompiledConfig):
|
|
258
|
+
agent_type = "TableRAGAgent"
|
|
259
|
+
k_recall: int = 50
|
|
260
|
+
k_rerank: int = 5
|
|
261
|
+
|
|
262
|
+
class TableRAGAgent(PrecompiledAgent):
|
|
263
|
+
config: TableRAGConfig # ! Important: config must be annotated with the config class
|
|
264
|
+
|
|
265
|
+
def __init__(self, config: TableRAGConfig, indexer: Indexer, **kwargs):
|
|
266
|
+
super().__init__(config, **kwargs)
|
|
267
|
+
self.indexer = indexer
|
|
268
|
+
# Initialize DSPy modules for reasoning
|
|
269
|
+
|
|
270
|
+
def forward(self, user_query: str) -> str:
|
|
271
|
+
# Retrieve relevant tables
|
|
272
|
+
# Generate SQL queries
|
|
273
|
+
# Combine results and provide answer
|
|
274
|
+
pass
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Support
|
|
278
|
+
|
|
279
|
+
For issues and questions:
|
|
280
|
+
- GitHub Issues: `https://github.com/modaic-ai/modaic/issues`
|
|
281
|
+
- Docs: `https://docs.modaic.dev`
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
modaic/__init__.py,sha256=rf2O0S7OAz5fBkmOcwygsM3u8Nmq_ios01ToNweNnSk,639
|
|
2
|
+
modaic/auto_agent.py,sha256=jegs8HMbE5OICZIDtBrP4kIma_R7rzy-1Kgmb_-eCck,8695
|
|
3
|
+
modaic/exceptions.py,sha256=XxzxOWjZTzT3l1BqTr7coJnVGxJq53uppRNrqP__YGo,651
|
|
4
|
+
modaic/hub.py,sha256=iWvWjaZxurd2BRXTS2gjhBvqVE1TDtwF0N7Falwj04Q,10527
|
|
5
|
+
modaic/indexing.py,sha256=L0O5yV7AhDUJ0gMyGE17BvHN2gwHxwkOMaxNTkyWQ8g,4185
|
|
6
|
+
modaic/module_utils.py,sha256=DDXUmcGFdaah_EhTlfdHC26ohmMwLNlIYo6PzYzKzqc,10728
|
|
7
|
+
modaic/observability.py,sha256=QEjLbmsVQzWZuxKQU8TBMSHsHVGvTzOeoNlEn_srmyg,9955
|
|
8
|
+
modaic/precompiled.py,sha256=g0AsFrHxzTlDETDqxZFqjHaLqW6m5OpGGyI01xDBT5U,15788
|
|
9
|
+
modaic/query_language.py,sha256=c-La7jYhHgNyjlQaxz0ALvUoiCDGzH9DpSD_9cozxNQ,10463
|
|
10
|
+
modaic/types.py,sha256=sHJ7J9YGfWIkDPfCuRc-n4O9n7g7LNnKSZperAviRFc,9905
|
|
11
|
+
modaic/utils.py,sha256=zbeMlrP_hoo8JUKR_bcuYPxrlYcckY3p0KJ_h4CN5VQ,721
|
|
12
|
+
modaic/agents/rag_agent.py,sha256=f8s3EILOPUxMpOKDoAvk-cfLE8S9kFNvkEcAC5z2EmQ,798
|
|
13
|
+
modaic/agents/registry.py,sha256=z6GuPxGrq2dinCamiMJ_HVPsD9Tp9XWDUSMZ-uhWPrU,2446
|
|
14
|
+
modaic/context/__init__.py,sha256=WTBo-WqhgeR84P3MEq0snLZyIHnLyv9VYO5Wfp4vrZo,534
|
|
15
|
+
modaic/context/base.py,sha256=QrRNBs05fb5EXN4intHT4D-XIG76RzvrO6j8RXJpzd4,40380
|
|
16
|
+
modaic/context/dtype_mapping.py,sha256=xRasW-H92YEuOfH8SbsVnodM9F-90pazott8qF2GWHw,519
|
|
17
|
+
modaic/context/table.py,sha256=c0OUVwglaDRtJ2Uo9z7bAKhIJpJXqwWk1VkKwQNOvUY,17696
|
|
18
|
+
modaic/context/text.py,sha256=S8E-dbTe-Wip8KCZ5vcIOZVyiVQReIL4bSo0anQw828,2581
|
|
19
|
+
modaic/databases/__init__.py,sha256=-w_yiY-Sqi1SgcPD5oAQL7MU4VXTihPa1GYGlrHfsFw,784
|
|
20
|
+
modaic/databases/graph_database.py,sha256=vMCYQrnBu5AIIGF_akkU9lWKFdbIDF3tlcyxiNQ_vSQ,9933
|
|
21
|
+
modaic/databases/sql_database.py,sha256=wqy7AqsalhmYsbNPy0FCAg1FrUKN6Bd8ytwyJireC94,12057
|
|
22
|
+
modaic/databases/vector_database/__init__.py,sha256=sN1SuSAMC9NHJDOa80BN_olccaHgmiW2Ek57hBvdZWo,306
|
|
23
|
+
modaic/databases/vector_database/vector_database.py,sha256=RsuRemgFV06opY26CekqLLRoAEFYOGl_CMuFETrYS0c,25238
|
|
24
|
+
modaic/databases/vector_database/benchmarks/baseline.py,sha256=ZhiYzHnizsLesAIQA93RhWaaYxEZp6ygExmnBhU9Dio,5209
|
|
25
|
+
modaic/databases/vector_database/benchmarks/common.py,sha256=fgPfnv1P55YAB5S6mORIpDMtKymsCPBjuQNiO5qLbJQ,1386
|
|
26
|
+
modaic/databases/vector_database/benchmarks/fork.py,sha256=Q-2dnsxB4_28mfkGHS2Y9-f-Fksym1bbdTFP-r0AKTk,5566
|
|
27
|
+
modaic/databases/vector_database/benchmarks/threaded.py,sha256=s2s1GwIRYq9oon5nsF_DRa65leCYc5uws6a5oHijk1M,5195
|
|
28
|
+
modaic/databases/vector_database/vendors/milvus.py,sha256=YT6rhRhe4bEC3lsgddno8k6wi1HqawqhvXXVkNYs3pY,15732
|
|
29
|
+
modaic/databases/vector_database/vendors/mongodb.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
+
modaic/databases/vector_database/vendors/pinecone.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
+
modaic/databases/vector_database/vendors/qdrant.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
32
|
+
modaic/storage/__init__.py,sha256=Zs-Y_9jfYUE8XVp8z-El0ZXFM_ZVMqM9aQ6fgGPZsf8,131
|
|
33
|
+
modaic/storage/file_store.py,sha256=kSS7gTP_-16wR3Xgq3frF1BZ8Dw8N--kG4V9rrCXPcc,7315
|
|
34
|
+
modaic/storage/pickle_store.py,sha256=fu9jkmmKNE852Y4R1NhOFePLfd2gskhHSXxuq1G1S3I,778
|
|
35
|
+
modaic-0.1.0.dist-info/licenses/LICENSE,sha256=7LMx9j453Vz1DoQbFot8Uhp9SExF5wlOx7c8vw2qhsE,1333
|
|
36
|
+
modaic-0.1.0.dist-info/METADATA,sha256=9Q1xSwbRGQl6GKKm32POVFdBlprvBCCcKxtNsVMKFFM,8585
|
|
37
|
+
modaic-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
38
|
+
modaic-0.1.0.dist-info/top_level.txt,sha256=RXWGuF-TsW8-17DveTJMPRiAgg_Rf2mq5F3R7tNu6t8,7
|
|
39
|
+
modaic-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Modaic Inc
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
Additional Terms:
|
|
26
|
+
|
|
27
|
+
1. You may not modify this Software in any way that changes the default hub
|
|
28
|
+
endpoint, nor distribute derivative works that route agents or models to
|
|
29
|
+
a hub other than modaic.dev.
|
|
30
|
+
|
|
31
|
+
2. All other rights are granted as per the MIT License.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
modaic
|