modaic 0.10.4__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.
@@ -0,0 +1,104 @@
1
+ # registry.py
2
+ import warnings
3
+ from importlib import import_module
4
+ from typing import Callable, Dict, NamedTuple, Tuple, Type
5
+
6
+
7
+ class Key(NamedTuple):
8
+ name: str
9
+ type: str # yes, attribute name 'type' is fine here
10
+
11
+
12
+ class Registry:
13
+ def __init__(self):
14
+ self._paths: Dict[Key, Tuple[str, str]] = {} # Key -> (module_path, qualname)
15
+ self._cache: Dict[Key, Type] = {}
16
+ self._frozen: bool = False
17
+
18
+ def register(self, key: Key, cls: Type) -> None:
19
+ if self._frozen:
20
+ raise RuntimeError("Registry is frozen; no further registrations allowed.")
21
+ if key in self._paths:
22
+ mod, qual = self._paths[key]
23
+ raise KeyError(f"Collision for {key}: already registered to {mod}:{qual}")
24
+ if not isinstance(cls, type):
25
+ raise TypeError("register() expects a class as the second argument.")
26
+
27
+ module_path = cls.__module__
28
+ qualname = cls.__qualname__ # supports nested classes
29
+ self._paths[key] = (module_path, qualname)
30
+ self._cache.pop(key, None) # just in case
31
+
32
+ def freeze(self) -> None:
33
+ self._frozen = True
34
+
35
+ def get(self, key: Key) -> Type:
36
+ # Fast path
37
+ if key in self._cache:
38
+ return self._cache[key]
39
+
40
+ try:
41
+ module_path, qualname = self._paths[key]
42
+ except KeyError:
43
+ raise KeyError(f"Unknown key {key}. Was it registered before freeze()?") from None
44
+
45
+ mod = import_module(module_path)
46
+ obj = mod
47
+ for part in qualname.split("."): # walk nested qualnames safely
48
+ obj = getattr(obj, part)
49
+
50
+ if not isinstance(obj, type):
51
+ raise TypeError(f"Resolved {module_path}:{qualname} is not a class.")
52
+
53
+ self._cache[key] = obj
54
+ return obj
55
+
56
+
57
+ # Instantiate per “kind”
58
+ ProgramRegistry = Registry()
59
+
60
+
61
+ def builtin_program(name: str) -> Callable[[Type], Type]:
62
+ """Decorator to register a builtin module."""
63
+
64
+ def _wrap(cls: Type) -> Type:
65
+ key = Key(name, "program")
66
+ ProgramRegistry.register(key, cls)
67
+ return cls
68
+
69
+ return _wrap
70
+
71
+
72
+ def builtin_agent(name: str) -> Callable[[Type], Type]:
73
+ """Deprecated: Use builtin_program instead."""
74
+ warnings.warn(
75
+ "builtin_agent is deprecated and will be removed in a future version. "
76
+ "Please use builtin_program instead for better parity with DSPy.",
77
+ DeprecationWarning,
78
+ stacklevel=2,
79
+ )
80
+
81
+ def _wrap(cls: Type) -> Type:
82
+ key = Key(name, "program")
83
+ ProgramRegistry.register(key, cls)
84
+ return cls
85
+
86
+ return _wrap
87
+
88
+
89
+ def builtin_indexer(name: str) -> Callable[[Type], Type]:
90
+ def _wrap(cls: Type) -> Type:
91
+ key = Key(name, "indexer")
92
+ ProgramRegistry.register(key, cls)
93
+ return cls
94
+
95
+ return _wrap
96
+
97
+
98
+ def builtin_config(name: str) -> Callable[[Type], Type]:
99
+ def _wrap(cls: Type) -> Type:
100
+ key = Key(name, "config")
101
+ ProgramRegistry.register(key, cls)
102
+ return cls
103
+
104
+ return _wrap
modaic/serializers.py ADDED
@@ -0,0 +1,222 @@
1
+ import inspect
2
+ import typing as t
3
+ from typing import TYPE_CHECKING, Annotated, Optional, Tuple, Type
4
+
5
+ import dspy
6
+ from dspy import InputField, OutputField, make_signature
7
+ from pydantic import BeforeValidator, Field, InstanceOf, PlainSerializer, create_model
8
+ from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue
9
+
10
+ if TYPE_CHECKING:
11
+ from pydantic.json_schema import CoreSchemaOrField
12
+
13
+ INCLUDED_FIELD_KWARGS = {
14
+ "desc",
15
+ "alias",
16
+ "alias_priority",
17
+ "validation_alias",
18
+ "serialization_alias",
19
+ "title",
20
+ "description",
21
+ "exclude",
22
+ "discriminator",
23
+ "deprecated",
24
+ "frozen",
25
+ "validate_default",
26
+ "repr",
27
+ "init",
28
+ "init_var",
29
+ "kw_only",
30
+ "pattern",
31
+ "strict",
32
+ "coerce_numbers_to_str",
33
+ "gt",
34
+ "ge",
35
+ "lt",
36
+ "le",
37
+ "multiple_of",
38
+ "allow_inf_nan",
39
+ "max_digits",
40
+ "decimal_places",
41
+ "min_length",
42
+ "max_length",
43
+ "union_mode",
44
+ "fail_fast",
45
+ }
46
+
47
+
48
+ def _handle_any_of(obj: dict, defs: Optional[dict] = None) -> t.Type:
49
+ """
50
+ Deserializes anyOf into a union type
51
+ """
52
+ return t.Union[tuple(json_to_type(item, defs) for item in obj["anyOf"])]
53
+
54
+
55
+ def _handle_object(obj: dict, defs: Optional[dict] = None) -> dict:
56
+ """
57
+ Deserializes basic objects types into dict type
58
+ """
59
+ additional_properties = obj.get("additionalProperties")
60
+ if additional_properties == True: # noqa: E712 we need to expliclity check for True, not just truthy
61
+ return dict
62
+ value_type = json_to_type(additional_properties, defs)
63
+ return dict[str, value_type]
64
+
65
+
66
+ def _handle_array(obj: dict, defs: Optional[dict] = None) -> list:
67
+ """
68
+ Deserializes arrays into lists, sets, or tuple type.
69
+ """
70
+ if (items := obj.get("items")) is not None:
71
+ set_or_list = set if obj.get("uniqueItems") else list
72
+
73
+ if items == {}:
74
+ return set_or_list
75
+ else:
76
+ return set_or_list[json_to_type(items, defs)]
77
+
78
+ elif "maxItems" in obj and "minItems" in obj and (prefix_items := obj.get("prefixItems")):
79
+ item_types = tuple(json_to_type(item, defs) for item in prefix_items)
80
+ return Tuple[item_types]
81
+ else:
82
+ raise ValueError(f"Invalid array: {obj}")
83
+
84
+
85
+ def _handle_custom_type(ref: str, defs: Optional[dict] = None) -> t.Type:
86
+ """
87
+ Deserializes custom types defined in $def into dspy special types and BaseModels
88
+ """
89
+ # CAVEAT if user defines custom types that overlap with these names they will be overwritten by the dspy types
90
+ dspy_types = {
91
+ "dspy.Image": dspy.Image,
92
+ "dspy.Audio": dspy.Audio,
93
+ "dspy.History": dspy.History,
94
+ "dspy.Tool": dspy.Tool,
95
+ "dspy.ToolCalls": dspy.ToolCalls,
96
+ "dspy.Code": dspy.Code,
97
+ }
98
+ name = ref.split("/")[-1]
99
+ obj = defs[name]
100
+ if dspy_type := dspy_types.get(obj["type"]):
101
+ return dspy_type
102
+ if obj["type"] == "object":
103
+ fields = {}
104
+ for name, field in obj["properties"].items():
105
+ field_kwargs = {k: v for k, v in field.items() if k in INCLUDED_FIELD_KWARGS}
106
+ if default := field.get("default"):
107
+ fields[name] = (
108
+ json_to_type(field, defs),
109
+ Field(default=default, **field_kwargs),
110
+ )
111
+ else:
112
+ fields[name] = (json_to_type(field, defs), Field(..., **field_kwargs))
113
+ return create_model(name, **fields)
114
+
115
+ else:
116
+ raise ValueError(f"Invalid type: {obj}")
117
+
118
+
119
+ def json_to_type(json_type: dict, defs: Optional[dict] = None) -> t.Type:
120
+ """
121
+ Desserializes a json schema into a python type
122
+ """
123
+ primitive_types = {
124
+ "string": str,
125
+ "number": float,
126
+ "integer": int,
127
+ "boolean": bool,
128
+ "null": None,
129
+ }
130
+ if j_type := json_type.get("type"):
131
+ if j_type in primitive_types:
132
+ return primitive_types[j_type]
133
+ elif j_type == "array":
134
+ return _handle_array(json_type, defs)
135
+ elif j_type == "object":
136
+ return _handle_object(json_type, defs)
137
+ else:
138
+ raise ValueError(f"Invalid type: {j_type}")
139
+ elif ref := json_type.get("$ref"):
140
+ return _handle_custom_type(ref, defs)
141
+ elif json_type.get("anyOf"):
142
+ return _handle_any_of(json_type, defs)
143
+ else:
144
+ raise ValueError(f"Invalid json schema: {json_type}")
145
+
146
+
147
+ def _deserialize_dspy_signatures(
148
+ obj: dict | Type[dspy.Signature],
149
+ ) -> Type[dspy.Signature]:
150
+ """
151
+ Deserizlizes a dictionary into a DSPy signature. Not all signatures can be deserialized.
152
+ - All fields (and fields of fields) cannot have default factories
153
+ - Frozensets will be serialized to sets
154
+ - tuples without arguments will be serialized to lists
155
+ """
156
+ if inspect.isclass(obj) and issubclass(obj, dspy.Signature):
157
+ return obj
158
+ fields = {}
159
+ defs = obj.get("$defs", {})
160
+ properties: dict[str, dict] = obj.get("properties", {})
161
+ for name, field in properties.items():
162
+ field_kwargs = {k: v for k, v in field.items() if k in INCLUDED_FIELD_KWARGS}
163
+ InputOrOutputField = InputField if field.get("__dspy_field_type") == "input" else OutputField # noqa: N806
164
+ if default := field.get("default"):
165
+ fields[name] = (
166
+ json_to_type(field, defs),
167
+ InputOrOutputField(default=default, **field_kwargs),
168
+ )
169
+ else:
170
+ fields[name] = (
171
+ json_to_type(field, defs),
172
+ InputOrOutputField(**field_kwargs),
173
+ )
174
+ signature = make_signature(
175
+ signature=fields,
176
+ instructions=obj.get("description"),
177
+ signature_name=obj.get("title"),
178
+ )
179
+ return signature
180
+
181
+
182
+ class DSPyTypeSchemaGenerator(GenerateJsonSchema):
183
+ def generate_inner(self, schema: "CoreSchemaOrField") -> JsonSchemaValue:
184
+ cls = schema.get("cls")
185
+ super_generate_inner = super().generate_inner
186
+
187
+ def handle_dspy_type(name: str) -> dict:
188
+ schema["metadata"]["pydantic_js_functions"] = [lambda cls, core_schema: {"type": f"dspy.{name}"}]
189
+ return super_generate_inner(schema)
190
+
191
+ for dspy_type in [
192
+ dspy.Image,
193
+ dspy.Audio,
194
+ dspy.History,
195
+ dspy.Tool,
196
+ dspy.ToolCalls,
197
+ dspy.Code,
198
+ ]:
199
+ if cls is dspy_type:
200
+ return handle_dspy_type(dspy_type.__name__)
201
+ return super_generate_inner(schema)
202
+
203
+
204
+ def _deserialize_dspy_lm(lm: dict | dspy.LM) -> dspy.LM:
205
+ if type(lm) is dspy.LM:
206
+ return lm
207
+ if isinstance(lm, dict):
208
+ return dspy.LM(**lm)
209
+
210
+
211
+ SerializableSignature = Annotated[
212
+ Type[dspy.Signature],
213
+ BeforeValidator(_deserialize_dspy_signatures),
214
+ PlainSerializer(lambda s: s.model_json_schema(schema_generator=DSPyTypeSchemaGenerator)),
215
+ ]
216
+
217
+
218
+ SerializableLM = Annotated[
219
+ InstanceOf[dspy.LM],
220
+ BeforeValidator(_deserialize_dspy_lm),
221
+ PlainSerializer(lambda lm: lm.dump_state()),
222
+ ]
modaic/utils.py ADDED
@@ -0,0 +1,115 @@
1
+ import os
2
+ import re
3
+ import shutil
4
+ import subprocess
5
+ import sys
6
+ import time
7
+ from pathlib import Path
8
+
9
+ from dotenv import find_dotenv, load_dotenv
10
+ from platformdirs import user_cache_dir
11
+
12
+ INCLUDED_FIELD_KWARGS = {
13
+ "desc",
14
+ "alias",
15
+ "alias_priority",
16
+ "validation_alias",
17
+ "serialization_alias",
18
+ "title",
19
+ "description",
20
+ "exclude",
21
+ "discriminator",
22
+ "deprecated",
23
+ "frozen",
24
+ "validate_default",
25
+ "repr",
26
+ "init",
27
+ "init_var",
28
+ "kw_only",
29
+ "pattern",
30
+ "strict",
31
+ "coerce_numbers_to_str",
32
+ "gt",
33
+ "ge",
34
+ "lt",
35
+ "le",
36
+ "multiple_of",
37
+ "allow_inf_nan",
38
+ "max_digits",
39
+ "decimal_places",
40
+ "min_length",
41
+ "max_length",
42
+ "union_mode",
43
+ "fail_fast",
44
+ }
45
+
46
+ env_file = find_dotenv(usecwd=True)
47
+ load_dotenv(env_file)
48
+
49
+
50
+ def compute_cache_dir() -> Path:
51
+ """Return the cache directory used to stage internal modules."""
52
+ cache_dir_env = os.getenv("MODAIC_CACHE")
53
+ if sys.platform.startswith("win"):
54
+ default_cache_dir = Path(user_cache_dir("modaic", appauthor=False))
55
+ else:
56
+ default_cache_dir = Path(os.path.expanduser("~")) / ".cache" / "modaic"
57
+ cache_dir = Path(cache_dir_env).expanduser().resolve() if cache_dir_env else default_cache_dir.resolve()
58
+ cache_dir.mkdir(parents=True, exist_ok=True)
59
+ return cache_dir
60
+
61
+
62
+ def validate_project_name(text: str) -> bool:
63
+ """Letters, numbers, underscore, hyphen"""
64
+ assert bool(re.match(r"^[a-zA-Z0-9_]+$", text)), (
65
+ "Invalid project name. Must contain only letters, numbers, and underscore."
66
+ )
67
+
68
+
69
+ class Timer:
70
+ def __init__(self, name: str):
71
+ self.start_time = time.time()
72
+ self.name = name
73
+
74
+ def __enter__(self):
75
+ return self
76
+
77
+ def __exit__(self, exc_type, exc_value, traceback): # noqa: ANN001, ANN002, ANN003
78
+ self.done()
79
+
80
+ def done(self):
81
+ end_time = time.time()
82
+ print(f"{self.name}: {end_time - self.start_time}s") # noqa: T201
83
+
84
+
85
+ def smart_rmtree(path: Path, ignore_errors: bool = False) -> None:
86
+ """
87
+ Remove a directory and all its contents.
88
+ If on windows use rmdir with /s flag
89
+ If on mac/linux use rm -rf
90
+ """
91
+ if sys.platform.startswith("win"):
92
+ try:
93
+ shutil.rmtree(path, ignore_errors=False)
94
+ except PermissionError:
95
+ subprocess.run(["cmd", "/c", "rmdir", "/s", "/q", str(path)], check=not ignore_errors)
96
+ except Exception as e:
97
+ if not ignore_errors:
98
+ raise e
99
+ else:
100
+ shutil.rmtree(path, ignore_errors=ignore_errors)
101
+
102
+
103
+ def aggresive_rmtree(path: Path, missing_ok: bool = True) -> None:
104
+ try:
105
+ shutil.rmtree(path, ignore_errors=False)
106
+ except FileNotFoundError as e:
107
+ if not missing_ok:
108
+ raise e
109
+ except Exception as e:
110
+ if sys.platform.startswith("win"):
111
+ subprocess.run(["taskkill", "/F", "/IM", "git.exe"], capture_output=True, check=False)
112
+ time.sleep(0.5)
113
+ subprocess.run(["cmd", "/c", "rmdir", "/s", "/q", str(path)], capture_output=True, check=True)
114
+ else:
115
+ raise e
@@ -0,0 +1,138 @@
1
+ Metadata-Version: 2.4
2
+ Name: modaic
3
+ Version: 0.10.4
4
+ Summary: Modular Agent Infrastructure Collection, a python framework for managing and sharing DSPy agents
5
+ Project-URL: Homepage, https://github.com/modaic-ai/modaic
6
+ Project-URL: Modaic, https://www.modaic.dev
7
+ Project-URL: Docs, https://docs.modaic.dev
8
+ Author-email: Tyrin <tytodd@mit.edu>, Farouk <farouk@modaic.dev>
9
+ License: MIT License
10
+
11
+ Copyright (c) 2025 Modaic Inc
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+
31
+ ---
32
+
33
+ Additional Terms:
34
+
35
+ 1. You may not modify this Software in any way that changes the default hub
36
+ endpoint, nor distribute derivative works that route agents or models to
37
+ a hub other than modaic.dev.
38
+
39
+ 2. All other rights are granted as per the MIT License.
40
+ License-File: LICENSE
41
+ Requires-Python: >=3.10
42
+ Requires-Dist: dspy>=2.6.27
43
+ Requires-Dist: gitpython>=3.1.45
44
+ Requires-Dist: opik==1.8.42
45
+ Requires-Dist: platformdirs>=4.3.8
46
+ Requires-Dist: tomlkit>=0.13.3
47
+ Description-Content-Type: text/markdown
48
+
49
+ [![Docs](https://img.shields.io/badge/docs-available-brightgreen.svg)](https://docs.modaic.dev)
50
+ [![PyPI](https://img.shields.io/pypi/v/modaic)](https://pypi.org/project/modaic/)
51
+
52
+
53
+ # Modaic 🐙
54
+ Modular + Mosaic, a Python framework for composing and maintaining DSPy applications.
55
+
56
+ ## Key Features
57
+
58
+ - **Hub Support**: Load and share precompiled DSPY programs from Modaic Hub
59
+ - **Program Framework**: Precompiled and auto-loading DSPY programs
60
+ - **Automated LM Judge Alignment**: Continuously align your LM judges to your preferences while staying at the pareto frontier!
61
+
62
+ Never lose your progress again.
63
+ Save everything you need to compare and reproduce optimization runs with GEPA, MIPROv2, etc. - architecture, hyperparameters, precompiled prompts, predictions, git commits, and even datasets - in 5 minutes. Modaic is free for personal use and academic projects, and it's easy to get started.
64
+
65
+ ## Installation
66
+
67
+ ### Using uv (recommended)
68
+
69
+ ```bash
70
+ uv add modaic
71
+ ```
72
+
73
+ Optional (for hub operations):
74
+
75
+ ```bash
76
+ export MODAIC_TOKEN="<your-token>"
77
+ ```
78
+
79
+ ### Using pip
80
+ Please note that you will not be able to push DSPY programs to the Modaic Hub with pip.
81
+ ```bash
82
+ pip install modaic
83
+ ```
84
+ ## Quick Start
85
+
86
+ ### Creating a Simple Program
87
+
88
+ ```python
89
+ from modaic import PrecompiledProgram, PrecompiledConfig
90
+
91
+ class WeatherConfig(PrecompiledConfig):
92
+ weather: str = "sunny"
93
+
94
+ class WeatherProgram(PrecompiledProgram):
95
+ config: WeatherConfig
96
+
97
+ def __init__(self, config: WeatherConfig, **kwargs):
98
+ super().__init__(config, **kwargs)
99
+
100
+ def forward(self, query: str) -> str:
101
+ return f"The weather in {query} is {self.config.weather}."
102
+
103
+ weather_program = WeatherProgram(WeatherConfig())
104
+ print(weather_program(query="Tokyo"))
105
+ weather_program.push_to_hub("me/my-weather-program")
106
+ ```
107
+
108
+ Save and load locally:
109
+
110
+ ```python
111
+ weather_program.save_precompiled("./my-weather")
112
+
113
+ from modaic import AutoProgram, AutoConfig
114
+
115
+ cfg = AutoConfig.from_precompiled("./my-weather", local=True)
116
+ loaded = AutoProgram.from_precompiled("./my-weather", local=True)
117
+ print(loaded(query="Kyoto"))
118
+ ```
119
+
120
+ from hub:
121
+
122
+ ```python
123
+ from modaic import AutoProgram, AutoConfig
124
+
125
+ loaded = AutoProgram.from_precompiled("me/my-weather-program", rev="v2.0.0")
126
+ print(loaded(query="Kyoto"))
127
+ ```
128
+
129
+ ## Architecture
130
+ ### Program Types
131
+
132
+ 1. **PrecompiledProgram**: Statically defined programs with explicit configuration
133
+ 2. **AutoProgram**: Dynamically loaded programs from Modaic Hub or local repositories
134
+ ## Support
135
+
136
+ For issues and questions:
137
+ - GitHub Issues: `https://github.com/modaic-ai/modaic/issues`
138
+ - Docs: `https://docs.modaic.dev`
@@ -0,0 +1,19 @@
1
+ modaic/__init__.py,sha256=MhWutNJpPyUBxlAwXj5TaYs9NfslN6DBLSW5g-RxfO8,806
2
+ modaic/auto.py,sha256=cwbuK_MYas3sroR8w6sMDuEWpYXLfE1sPJ0siHTYaJY,10459
3
+ modaic/constants.py,sha256=4HElDBnhbeJYq5KHwcwBfOi50Ov3m5TmVdlu2P4qusc,586
4
+ modaic/datasets.py,sha256=K-PpPSYIxJI0-yH-SBVpk_EfCM9i_uPz-brmlzP7hzI,513
5
+ modaic/exceptions.py,sha256=tdtD8uKA7Tz0H0Q1mLU88mmqKA_X1MQjqdwN3gm62J4,1162
6
+ modaic/hub.py,sha256=AygZiICOmANFxAldZhOs4mWrnDdjJ6sSIuChaCWW7BQ,24285
7
+ modaic/module_utils.py,sha256=steEUjAMMR_iNsnMSdb4wJWOhABkfvplKcMxrXMZjDI,18401
8
+ modaic/observability.py,sha256=_oQ1XHoiGUxcJ4TJuVZdVbHVg_1090vbnrCpDX3baxU,9082
9
+ modaic/precompiled.py,sha256=OKVkS56xzZDQKemJHsLCPOrxoGUowqkEuN8fnQppLlo,22860
10
+ modaic/serializers.py,sha256=1IY2KnxQNZOFNWlyfMnWYYMqWp4aWokL1cqyWISwfGE,6989
11
+ modaic/utils.py,sha256=c3xJ1-HYgBrLWKxlWzM8_RNs0kP2wBoIGb7BTKQ1CTE,3109
12
+ modaic/programs/__init__.py,sha256=Wn-uv5Wd6XGkkvepGIg2R_Dsa9gDBp2Ylyft-n58e1I,58
13
+ modaic/programs/predict.py,sha256=-ybyGv1aT12aMIt8DFYRCn02HLqFVoSIrhcfkg6Xl90,1569
14
+ modaic/programs/rag_program.py,sha256=Hj3LahWgPRnXyz_SIjuNx4uU4mguCJlgTbHd3a62Sec,833
15
+ modaic/programs/registry.py,sha256=2HypXX6t9d00dhyUf8QoeOL6UJpmgN7g0RVQ0oYMCOg,3014
16
+ modaic-0.10.4.dist-info/METADATA,sha256=dzuqz7dQM4aPUhy_TdwEBj7rRHpi4vhokBLUlbOFeD4,4786
17
+ modaic-0.10.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
18
+ modaic-0.10.4.dist-info/licenses/LICENSE,sha256=7LMx9j453Vz1DoQbFot8Uhp9SExF5wlOx7c8vw2qhsE,1333
19
+ modaic-0.10.4.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -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.