llm-function 0.0.1__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.
- llm_function/.paa.tracking/.paa.config +12 -0
- llm_function/.paa.tracking/.paa.version +1 -0
- llm_function/.paa.tracking/git/dependencies/llm_func_deps/llm_function_config.py +35 -0
- llm_function/.paa.tracking/git/dependencies/llm_func_deps/tool_registry.py +250 -0
- llm_function/.paa.tracking/git/docs_cache/llm_function.md +0 -0
- llm_function/.paa.tracking/git/module/llm_function.py +250 -0
- llm_function/.paa.tracking/git/tracking/release_notes.md +5 -0
- llm_function/.paa.tracking/git_repo/COMMIT_EDITMSG +1 -0
- llm_function/.paa.tracking/git_repo/HEAD +1 -0
- llm_function/.paa.tracking/git_repo/config +8 -0
- llm_function/.paa.tracking/git_repo/description +1 -0
- llm_function/.paa.tracking/git_repo/hooks/applypatch-msg.sample +15 -0
- llm_function/.paa.tracking/git_repo/hooks/commit-msg.sample +24 -0
- llm_function/.paa.tracking/git_repo/hooks/fsmonitor-watchman.sample +174 -0
- llm_function/.paa.tracking/git_repo/hooks/post-update.sample +8 -0
- llm_function/.paa.tracking/git_repo/hooks/pre-applypatch.sample +14 -0
- llm_function/.paa.tracking/git_repo/hooks/pre-commit.sample +49 -0
- llm_function/.paa.tracking/git_repo/hooks/pre-merge-commit.sample +13 -0
- llm_function/.paa.tracking/git_repo/hooks/pre-push.sample +53 -0
- llm_function/.paa.tracking/git_repo/hooks/pre-rebase.sample +169 -0
- llm_function/.paa.tracking/git_repo/hooks/pre-receive.sample +24 -0
- llm_function/.paa.tracking/git_repo/hooks/prepare-commit-msg.sample +42 -0
- llm_function/.paa.tracking/git_repo/hooks/push-to-checkout.sample +78 -0
- llm_function/.paa.tracking/git_repo/hooks/sendemail-validate.sample +77 -0
- llm_function/.paa.tracking/git_repo/hooks/update.sample +128 -0
- llm_function/.paa.tracking/git_repo/index +0 -0
- llm_function/.paa.tracking/git_repo/info/exclude +6 -0
- llm_function/.paa.tracking/git_repo/logs/HEAD +1 -0
- llm_function/.paa.tracking/git_repo/logs/refs/heads/master +1 -0
- llm_function/.paa.tracking/git_repo/objects/13/9201e8ffeb4e42262e90eeaefd5267debb6e1d +1 -0
- llm_function/.paa.tracking/git_repo/objects/23/d10b479e3102724a6eb510225cc24bb2ce9d78 +0 -0
- llm_function/.paa.tracking/git_repo/objects/2d/fa3abff2848ff521ead3abb4364bd2c3ca01a6 +0 -0
- llm_function/.paa.tracking/git_repo/objects/45/6fc88abc846d76e0f82a3ef180425e497f8281 +0 -0
- llm_function/.paa.tracking/git_repo/objects/55/d196aad68e2cc20a2ea496529d9dabeb75bb0e +0 -0
- llm_function/.paa.tracking/git_repo/objects/63/9282ebc80b9db9b677374a7c16bc7472d5e71c +0 -0
- llm_function/.paa.tracking/git_repo/objects/91/f1d4c0830abe4ebb0902dc052f5dfe9e8fade7 +0 -0
- llm_function/.paa.tracking/git_repo/objects/9d/2a8151dd42e6abfffbbbd674b850a7066daa1a +0 -0
- llm_function/.paa.tracking/git_repo/objects/aa/0b3847c3f0683eed107a405b9443ec040c3eec +0 -0
- llm_function/.paa.tracking/git_repo/objects/cf/024b71ae427969a985d86a267adf154f526cd8 +0 -0
- llm_function/.paa.tracking/git_repo/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
- llm_function/.paa.tracking/git_repo/objects/fb/834f2f59b432c0688699b91926899c0e0f2a5d +0 -0
- llm_function/.paa.tracking/git_repo/refs/heads/master +1 -0
- llm_function/.paa.tracking/git_repo/refs/tags/v0.0.1 +1 -0
- llm_function/.paa.tracking/lsts_package_versions.yml +3 -0
- llm_function/.paa.tracking/package_licenses.json +1 -0
- llm_function/.paa.tracking/package_mapping.json +1 -0
- llm_function/.paa.tracking/python_modules/components/llm_func_deps/llm_function_config.py +35 -0
- llm_function/.paa.tracking/python_modules/components/llm_func_deps/tool_registry.py +250 -0
- llm_function/.paa.tracking/python_modules/llm_function.py +250 -0
- llm_function/.paa.tracking/release_notes.md +5 -0
- llm_function/.paa.tracking/version_logs.csv +5 -0
- llm_function/__init__.py +17 -0
- llm_function/artifacts/.paa.tracking/git/dependencies/llm_func_deps/llm_function_config.py +35 -0
- llm_function/artifacts/.paa.tracking/git/dependencies/llm_func_deps/tool_registry.py +250 -0
- llm_function/artifacts/.paa.tracking/git/docs_cache/llm_function.md +0 -0
- llm_function/artifacts/.paa.tracking/git/module/llm_function.py +250 -0
- llm_function/artifacts/.paa.tracking/git/tracking/release_notes.md +5 -0
- llm_function/artifacts/.paa.tracking/git_repo/COMMIT_EDITMSG +1 -0
- llm_function/artifacts/.paa.tracking/git_repo/HEAD +1 -0
- llm_function/artifacts/.paa.tracking/git_repo/config +8 -0
- llm_function/artifacts/.paa.tracking/git_repo/description +1 -0
- llm_function/artifacts/.paa.tracking/git_repo/hooks/applypatch-msg.sample +15 -0
- llm_function/artifacts/.paa.tracking/git_repo/hooks/commit-msg.sample +24 -0
- llm_function/artifacts/.paa.tracking/git_repo/hooks/fsmonitor-watchman.sample +174 -0
- llm_function/artifacts/.paa.tracking/git_repo/hooks/post-update.sample +8 -0
- llm_function/artifacts/.paa.tracking/git_repo/hooks/pre-applypatch.sample +14 -0
- llm_function/artifacts/.paa.tracking/git_repo/hooks/pre-commit.sample +49 -0
- llm_function/artifacts/.paa.tracking/git_repo/hooks/pre-merge-commit.sample +13 -0
- llm_function/artifacts/.paa.tracking/git_repo/hooks/pre-push.sample +53 -0
- llm_function/artifacts/.paa.tracking/git_repo/hooks/pre-rebase.sample +169 -0
- llm_function/artifacts/.paa.tracking/git_repo/hooks/pre-receive.sample +24 -0
- llm_function/artifacts/.paa.tracking/git_repo/hooks/prepare-commit-msg.sample +42 -0
- llm_function/artifacts/.paa.tracking/git_repo/hooks/push-to-checkout.sample +78 -0
- llm_function/artifacts/.paa.tracking/git_repo/hooks/sendemail-validate.sample +77 -0
- llm_function/artifacts/.paa.tracking/git_repo/hooks/update.sample +128 -0
- llm_function/artifacts/.paa.tracking/git_repo/index +0 -0
- llm_function/artifacts/.paa.tracking/git_repo/info/exclude +6 -0
- llm_function/artifacts/.paa.tracking/git_repo/logs/HEAD +1 -0
- llm_function/artifacts/.paa.tracking/git_repo/logs/refs/heads/master +1 -0
- llm_function/artifacts/.paa.tracking/git_repo/objects/13/9201e8ffeb4e42262e90eeaefd5267debb6e1d +1 -0
- llm_function/artifacts/.paa.tracking/git_repo/objects/23/d10b479e3102724a6eb510225cc24bb2ce9d78 +0 -0
- llm_function/artifacts/.paa.tracking/git_repo/objects/2d/fa3abff2848ff521ead3abb4364bd2c3ca01a6 +0 -0
- llm_function/artifacts/.paa.tracking/git_repo/objects/45/6fc88abc846d76e0f82a3ef180425e497f8281 +0 -0
- llm_function/artifacts/.paa.tracking/git_repo/objects/55/d196aad68e2cc20a2ea496529d9dabeb75bb0e +0 -0
- llm_function/artifacts/.paa.tracking/git_repo/objects/63/9282ebc80b9db9b677374a7c16bc7472d5e71c +0 -0
- llm_function/artifacts/.paa.tracking/git_repo/objects/91/f1d4c0830abe4ebb0902dc052f5dfe9e8fade7 +0 -0
- llm_function/artifacts/.paa.tracking/git_repo/objects/9d/2a8151dd42e6abfffbbbd674b850a7066daa1a +0 -0
- llm_function/artifacts/.paa.tracking/git_repo/objects/aa/0b3847c3f0683eed107a405b9443ec040c3eec +0 -0
- llm_function/artifacts/.paa.tracking/git_repo/objects/cf/024b71ae427969a985d86a267adf154f526cd8 +0 -0
- llm_function/artifacts/.paa.tracking/git_repo/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
- llm_function/artifacts/.paa.tracking/git_repo/objects/fb/834f2f59b432c0688699b91926899c0e0f2a5d +0 -0
- llm_function/artifacts/.paa.tracking/git_repo/refs/heads/master +1 -0
- llm_function/artifacts/.paa.tracking/git_repo/refs/tags/v0.0.1 +1 -0
- llm_function/llm_function.py +518 -0
- llm_function/mkdocs/docs/css/extra.css +38 -0
- llm_function/mkdocs/docs/index.md +24 -0
- llm_function/mkdocs/docs/llm_function_tools.md +168 -0
- llm_function/mkdocs/docs/release-notes.md +5 -0
- llm_function/mkdocs/mkdocs.yml +37 -0
- llm_function/mkdocs/site/404.html +370 -0
- llm_function/mkdocs/site/assets/images/favicon.png +0 -0
- llm_function/mkdocs/site/assets/javascripts/bundle.fe8b6f2b.min.js +29 -0
- llm_function/mkdocs/site/assets/javascripts/bundle.fe8b6f2b.min.js.map +7 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.ar.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.da.min.js +18 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.de.min.js +18 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.du.min.js +18 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.el.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.es.min.js +18 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.fi.min.js +18 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.fr.min.js +18 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.he.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.hi.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.hu.min.js +18 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.hy.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.it.min.js +18 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.ja.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.jp.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.kn.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.ko.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.multi.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.nl.min.js +18 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.no.min.js +18 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.pt.min.js +18 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.ro.min.js +18 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.ru.min.js +18 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.sa.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.sv.min.js +18 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.ta.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.te.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.th.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.tr.min.js +18 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.vi.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/min/lunr.zh.min.js +1 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/tinyseg.js +206 -0
- llm_function/mkdocs/site/assets/javascripts/lunr/wordcut.js +6708 -0
- llm_function/mkdocs/site/assets/javascripts/workers/search.b8dbb3d2.min.js +42 -0
- llm_function/mkdocs/site/assets/javascripts/workers/search.b8dbb3d2.min.js.map +7 -0
- llm_function/mkdocs/site/assets/stylesheets/main.3cba04c6.min.css +1 -0
- llm_function/mkdocs/site/assets/stylesheets/main.3cba04c6.min.css.map +1 -0
- llm_function/mkdocs/site/assets/stylesheets/palette.06af60db.min.css +1 -0
- llm_function/mkdocs/site/assets/stylesheets/palette.06af60db.min.css.map +1 -0
- llm_function/mkdocs/site/css/extra.css +38 -0
- llm_function/mkdocs/site/index.html +476 -0
- llm_function/mkdocs/site/llm_function_tools/index.html +622 -0
- llm_function/mkdocs/site/release-notes/index.html +468 -0
- llm_function/mkdocs/site/search/search_index.json +1 -0
- llm_function/mkdocs/site/sitemap.xml +3 -0
- llm_function/mkdocs/site/sitemap.xml.gz +0 -0
- llm_function/setup.py +47 -0
- llm_function-0.0.1.dist-info/METADATA +44 -0
- llm_function-0.0.1.dist-info/RECORD +157 -0
- llm_function-0.0.1.dist-info/WHEEL +5 -0
- llm_function-0.0.1.dist-info/licenses/LICENSE +201 -0
- llm_function-0.0.1.dist-info/licenses/NOTICE +4 -0
- llm_function-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
use_commit_messages: true
|
|
2
|
+
check_vulnerabilities: true
|
|
3
|
+
check_dependencies_licenses: false
|
|
4
|
+
add_artifacts: true
|
|
5
|
+
add_mkdocs_site: true
|
|
6
|
+
pylint_threshold: 8.0
|
|
7
|
+
license_label: 'apache-2.0'
|
|
8
|
+
allowed_licenses: ['mit', 'apache-2.0', 'lgpl-3.0',
|
|
9
|
+
'bsd-3-clause', 'bsd-2-clause', '-', 'mpl-2.0']
|
|
10
|
+
gh_pages_base_url: https://kiril-mordan.github.io/adaptive-reusables
|
|
11
|
+
source_repo_url: https://github.com/Kiril-Mordan/adaptive-reusables
|
|
12
|
+
source_repo_name: adaptive-reusables
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.0.4
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Config models for llm_function runtime wiring.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional, Sequence
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class LlmRuntimeConfig(BaseModel):
|
|
11
|
+
"""
|
|
12
|
+
Reusable runtime settings for llm_function execution.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
llm_handler_params: dict = Field(description="Parameters passed to WorkflowAutoAssembler LLM handler.")
|
|
16
|
+
storage_path: Optional[str] = Field(default=None, description="Optional storage path for workflow persistence.")
|
|
17
|
+
force_replan: bool = Field(default=False, description="Force workflow replanning instead of reusing cached workflows.")
|
|
18
|
+
max_retry: Optional[int] = Field(default=None, description="Optional max retry override for planning loops.")
|
|
19
|
+
reset_loops: Optional[int] = Field(default=None, description="Optional reset loop override for planning loops.")
|
|
20
|
+
compare_params: Optional[dict] = Field(default=None, description="Optional compare parameters for workflow validation.")
|
|
21
|
+
test_params: Optional[list] = Field(default=None, description="Optional test cases for planning/validation.")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class LlmFunctionConfig(BaseModel):
|
|
25
|
+
"""
|
|
26
|
+
Bundled llm_function configuration for future config-driven decorator usage.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
runtime: LlmRuntimeConfig = Field(description="Runtime settings for workflow execution.")
|
|
30
|
+
tool_sources: Optional[Sequence[Any]] = Field(default=None, description="Configured tool sources for runtime loading.")
|
|
31
|
+
tool_registry: Optional[Any] = Field(default=None, description="Optional prebuilt tool registry.")
|
|
32
|
+
|
|
33
|
+
model_config = {
|
|
34
|
+
"arbitrary_types_allowed": True,
|
|
35
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Runtime tool loading helpers for llm_function.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import attrs
|
|
6
|
+
import attrsx
|
|
7
|
+
import inspect
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Callable, Dict, List, Optional, Sequence, Union
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, Field, SkipValidation
|
|
12
|
+
from workflow_auto_assembler import LlmFunctionItem, make_uid
|
|
13
|
+
|
|
14
|
+
from llm_function_tools import (
|
|
15
|
+
ToolSpec,
|
|
16
|
+
load_tools_from_module,
|
|
17
|
+
load_tools_from_python_file,
|
|
18
|
+
tool_from_callable,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ResolvedTool(BaseModel):
|
|
23
|
+
"""
|
|
24
|
+
Runtime-ready tool definition with provenance metadata.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
tool_spec: ToolSpec = Field(description="Normalized tool definition.")
|
|
28
|
+
func: SkipValidation[Callable[..., Any]] = Field(description="Runtime callable for the tool.")
|
|
29
|
+
source_type: str = Field(description="Source kind, for example file or module.")
|
|
30
|
+
location_type: str = Field(description="Location kind, for example local or external.")
|
|
31
|
+
package_name: Optional[str] = Field(default=None, description="Optional package name.")
|
|
32
|
+
package_version: Optional[str] = Field(default=None, description="Optional resolved package version.")
|
|
33
|
+
module_name: Optional[str] = Field(default=None, description="Python module that defined the tool.")
|
|
34
|
+
file_path: Optional[str] = Field(default=None, description="Local file path when the tool was loaded from a file.")
|
|
35
|
+
origin_ref: Optional[str] = Field(default=None, description="Original source reference used to load the tool.")
|
|
36
|
+
metadata: dict = Field(default_factory=dict, description="Additional runtime metadata.")
|
|
37
|
+
|
|
38
|
+
model_config = {
|
|
39
|
+
"arbitrary_types_allowed": True,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@attrsx.define
|
|
44
|
+
class InMemoryToolSource:
|
|
45
|
+
"""
|
|
46
|
+
Resolve tools directly from callables, ToolSpec objects, or ResolvedTool objects.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
tools: Sequence[Union[Callable[..., Any], ToolSpec, ResolvedTool]] = attrs.field()
|
|
50
|
+
location_type: str = attrs.field(default="local")
|
|
51
|
+
package_name: Optional[str] = attrs.field(default=None)
|
|
52
|
+
package_version: Optional[str] = attrs.field(default=None)
|
|
53
|
+
origin_ref: Optional[str] = attrs.field(default=None)
|
|
54
|
+
|
|
55
|
+
def __attrs_post_init__(self):
|
|
56
|
+
self.tools = list(self.tools)
|
|
57
|
+
|
|
58
|
+
def load_tools(self) -> List[ResolvedTool]:
|
|
59
|
+
"""
|
|
60
|
+
Resolve in-memory tools into runtime tool records.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
resolved_tools: List[ResolvedTool] = []
|
|
64
|
+
for item in self.tools:
|
|
65
|
+
if isinstance(item, ResolvedTool):
|
|
66
|
+
resolved_tools.append(item)
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
tool_spec = item if isinstance(item, ToolSpec) else tool_from_callable(item)
|
|
70
|
+
resolved_tools.append(
|
|
71
|
+
ResolvedTool(
|
|
72
|
+
tool_spec=tool_spec,
|
|
73
|
+
func=tool_spec.func,
|
|
74
|
+
source_type="memory",
|
|
75
|
+
location_type=self.location_type,
|
|
76
|
+
package_name=self.package_name,
|
|
77
|
+
package_version=self.package_version,
|
|
78
|
+
module_name=getattr(tool_spec.func, "__module__", None),
|
|
79
|
+
origin_ref=self.origin_ref,
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return resolved_tools
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@attrsx.define
|
|
87
|
+
class PythonModuleToolSource:
|
|
88
|
+
"""
|
|
89
|
+
Resolve tools from an importable Python module.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
module_name: str = attrs.field()
|
|
93
|
+
include_plain_typed: bool = attrs.field(default=False)
|
|
94
|
+
location_type: str = attrs.field(default="local")
|
|
95
|
+
package_name: Optional[str] = attrs.field(default=None)
|
|
96
|
+
package_version: Optional[str] = attrs.field(default=None)
|
|
97
|
+
|
|
98
|
+
def load_tools(self) -> List[ResolvedTool]:
|
|
99
|
+
"""
|
|
100
|
+
Import module and resolve tools declared inside it.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
resolved_tools: List[ResolvedTool] = []
|
|
104
|
+
for tool_spec in load_tools_from_module(
|
|
105
|
+
self.module_name,
|
|
106
|
+
include_plain_typed=self.include_plain_typed,
|
|
107
|
+
):
|
|
108
|
+
resolved_tools.append(
|
|
109
|
+
ResolvedTool(
|
|
110
|
+
tool_spec=tool_spec,
|
|
111
|
+
func=tool_spec.func,
|
|
112
|
+
source_type="module",
|
|
113
|
+
location_type=self.location_type,
|
|
114
|
+
package_name=self.package_name,
|
|
115
|
+
package_version=self.package_version,
|
|
116
|
+
module_name=self.module_name,
|
|
117
|
+
origin_ref=self.module_name,
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
return resolved_tools
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@attrsx.define
|
|
125
|
+
class PythonFileToolSource:
|
|
126
|
+
"""
|
|
127
|
+
Resolve tools from a standalone Python file path.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
file_path: str = attrs.field()
|
|
131
|
+
include_plain_typed: bool = attrs.field(default=False)
|
|
132
|
+
location_type: str = attrs.field(default="local")
|
|
133
|
+
package_name: Optional[str] = attrs.field(default=None)
|
|
134
|
+
package_version: Optional[str] = attrs.field(default=None)
|
|
135
|
+
module_name: Optional[str] = attrs.field(default=None)
|
|
136
|
+
|
|
137
|
+
def load_tools(self) -> List[ResolvedTool]:
|
|
138
|
+
"""
|
|
139
|
+
Import file and resolve tools declared inside it.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
resolved_path = str(Path(self.file_path).expanduser().resolve())
|
|
143
|
+
resolved_tools: List[ResolvedTool] = []
|
|
144
|
+
for tool_spec in load_tools_from_python_file(
|
|
145
|
+
resolved_path,
|
|
146
|
+
include_plain_typed=self.include_plain_typed,
|
|
147
|
+
module_name=self.module_name,
|
|
148
|
+
):
|
|
149
|
+
resolved_tools.append(
|
|
150
|
+
ResolvedTool(
|
|
151
|
+
tool_spec=tool_spec,
|
|
152
|
+
func=tool_spec.func,
|
|
153
|
+
source_type="file",
|
|
154
|
+
location_type=self.location_type,
|
|
155
|
+
package_name=self.package_name,
|
|
156
|
+
package_version=self.package_version,
|
|
157
|
+
module_name=getattr(tool_spec.func, "__module__", self.module_name),
|
|
158
|
+
file_path=resolved_path,
|
|
159
|
+
origin_ref=resolved_path,
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
return resolved_tools
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@attrsx.define
|
|
167
|
+
class ToolRegistry:
|
|
168
|
+
"""
|
|
169
|
+
Aggregate tools from one or more sources and expose WAA-compatible tool lists.
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
sources: Sequence[object] = attrs.field()
|
|
173
|
+
_resolved_tools: Optional[List[ResolvedTool]] = attrs.field(default=None)
|
|
174
|
+
|
|
175
|
+
def __attrs_post_init__(self):
|
|
176
|
+
self.sources = list(self.sources)
|
|
177
|
+
|
|
178
|
+
def load_tools(self, reload: bool = False) -> List[ResolvedTool]:
|
|
179
|
+
"""
|
|
180
|
+
Resolve all configured sources into runtime tools.
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
if self._resolved_tools is not None and not reload:
|
|
184
|
+
return list(self._resolved_tools)
|
|
185
|
+
|
|
186
|
+
resolved_tools: List[ResolvedTool] = []
|
|
187
|
+
seen_keys = set()
|
|
188
|
+
|
|
189
|
+
for source in self.sources:
|
|
190
|
+
for resolved_tool in source.load_tools():
|
|
191
|
+
dedupe_key = (
|
|
192
|
+
resolved_tool.tool_spec.name,
|
|
193
|
+
resolved_tool.module_name,
|
|
194
|
+
resolved_tool.file_path,
|
|
195
|
+
resolved_tool.package_name,
|
|
196
|
+
resolved_tool.package_version,
|
|
197
|
+
)
|
|
198
|
+
if dedupe_key in seen_keys:
|
|
199
|
+
continue
|
|
200
|
+
seen_keys.add(dedupe_key)
|
|
201
|
+
resolved_tools.append(resolved_tool)
|
|
202
|
+
|
|
203
|
+
self._resolved_tools = resolved_tools
|
|
204
|
+
return list(self._resolved_tools)
|
|
205
|
+
|
|
206
|
+
@staticmethod
|
|
207
|
+
def _safe_source_code(func: Callable[..., Any]) -> str:
|
|
208
|
+
"""
|
|
209
|
+
Best-effort source capture for stable tool hashing.
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
try:
|
|
213
|
+
return inspect.getsource(func)
|
|
214
|
+
except (OSError, TypeError):
|
|
215
|
+
return getattr(func, "__qualname__", getattr(func, "__name__", ""))
|
|
216
|
+
|
|
217
|
+
def build_available_tools(self) -> Dict[str, Any]:
|
|
218
|
+
"""
|
|
219
|
+
Convert resolved tools into WorkflowAutoAssembler-compatible structures.
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
available_functions: List[LlmFunctionItem] = []
|
|
223
|
+
available_callables: Dict[str, Callable[..., Any]] = {}
|
|
224
|
+
|
|
225
|
+
for resolved_tool in self.load_tools():
|
|
226
|
+
llm_func_item = {
|
|
227
|
+
"name": resolved_tool.tool_spec.name,
|
|
228
|
+
"description": resolved_tool.tool_spec.description,
|
|
229
|
+
"input_schema_json": resolved_tool.tool_spec.input_model.model_json_schema(),
|
|
230
|
+
"output_schema_json": resolved_tool.tool_spec.output_model.model_json_schema(),
|
|
231
|
+
"code": self._safe_source_code(resolved_tool.func),
|
|
232
|
+
}
|
|
233
|
+
func_id = make_uid(d=llm_func_item)
|
|
234
|
+
|
|
235
|
+
available_functions.append(
|
|
236
|
+
LlmFunctionItem(
|
|
237
|
+
func_id=func_id,
|
|
238
|
+
name=llm_func_item["name"],
|
|
239
|
+
description=llm_func_item["description"],
|
|
240
|
+
input_schema_json=llm_func_item["input_schema_json"],
|
|
241
|
+
output_schema_json=llm_func_item["output_schema_json"],
|
|
242
|
+
)
|
|
243
|
+
)
|
|
244
|
+
available_callables[func_id] = resolved_tool.func
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
"available_functions": available_functions,
|
|
248
|
+
"available_callables": available_callables,
|
|
249
|
+
"resolved_tools": self.load_tools(),
|
|
250
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""
|
|
2
|
+
`llm_function` helps you build reusable LLM functions with normal Python signatures.
|
|
3
|
+
|
|
4
|
+
You define a function with Pydantic input and output models, describe what it should do
|
|
5
|
+
in the docstring, and provide a set of available tools. At runtime, `llm_function`
|
|
6
|
+
uses [`workflow_auto_assembler`](https://pypi.org/project/workflow-auto-assembler/) to
|
|
7
|
+
assemble and execute a workflow that satisfies that typed function contract.
|
|
8
|
+
|
|
9
|
+
The result is an LLM-backed function that can be reused like any other Python function,
|
|
10
|
+
while still being grounded in explicit tools, schemas, and config.
|
|
11
|
+
|
|
12
|
+
Tool definition and discovery live in
|
|
13
|
+
[`llm_function_tools`](https://pypi.org/project/llm-function-tools/).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import asyncio
|
|
17
|
+
import attrs
|
|
18
|
+
import attrsx
|
|
19
|
+
import inspect
|
|
20
|
+
import threading
|
|
21
|
+
from functools import wraps
|
|
22
|
+
from typing import Any, Callable, Dict, List, Optional, Type, get_type_hints
|
|
23
|
+
|
|
24
|
+
from pydantic import BaseModel
|
|
25
|
+
from workflow_auto_assembler import WorkflowAutoAssembler
|
|
26
|
+
from .components.llm_func_deps.llm_function_config import LlmFunctionConfig, LlmRuntimeConfig
|
|
27
|
+
from .components.llm_func_deps.tool_registry import InMemoryToolSource, ToolRegistry
|
|
28
|
+
|
|
29
|
+
__package_metadata__ = {
|
|
30
|
+
"author": "Kyrylo Mordan",
|
|
31
|
+
"author_email": "parachute.repo@gmail.com",
|
|
32
|
+
"description": "Llm function is a decorator that uses llm to assemble reusable workflows from available tools to match input and output models.",
|
|
33
|
+
"url" : 'https://kiril-mordan.github.io/adaptive-reusables/llm-function/',
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@attrsx.define
|
|
38
|
+
class LlmFunction:
|
|
39
|
+
available_functions: Optional[List[Any]] = attrs.field(default=None)
|
|
40
|
+
available_callables: Optional[Dict[str, Callable[..., Any]]] = attrs.field(default=None)
|
|
41
|
+
tool_registry: Optional[ToolRegistry] = attrs.field(default=None)
|
|
42
|
+
tool_sources: Optional[List[object]] = attrs.field(default=None)
|
|
43
|
+
config: Optional[LlmFunctionConfig] = attrs.field(default=None)
|
|
44
|
+
llm_handler_params: Optional[dict] = attrs.field(default=None)
|
|
45
|
+
storage_path: Optional[str] = attrs.field(default=None)
|
|
46
|
+
force_replan: bool = attrs.field(default=False)
|
|
47
|
+
max_retry: Optional[int] = attrs.field(default=None)
|
|
48
|
+
reset_loops: Optional[int] = attrs.field(default=None)
|
|
49
|
+
compare_params: Optional[dict] = attrs.field(default=None)
|
|
50
|
+
test_params: Optional[list] = attrs.field(default=None)
|
|
51
|
+
resolved_tools: Optional[Dict[str, Any]] = attrs.field(default=None, init=False)
|
|
52
|
+
|
|
53
|
+
def __attrs_post_init__(self):
|
|
54
|
+
self._apply_config_defaults()
|
|
55
|
+
|
|
56
|
+
if self.llm_handler_params is None:
|
|
57
|
+
raise ValueError("llm_handler_params is required either directly or via config.")
|
|
58
|
+
|
|
59
|
+
self.resolved_tools = self._resolve_available_tools()
|
|
60
|
+
|
|
61
|
+
def _apply_config_defaults(self):
|
|
62
|
+
if self.config is None:
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
if self.llm_handler_params is None:
|
|
66
|
+
self.llm_handler_params = self.config.runtime.llm_handler_params
|
|
67
|
+
if self.storage_path is None:
|
|
68
|
+
self.storage_path = self.config.runtime.storage_path
|
|
69
|
+
if self.force_replan is False:
|
|
70
|
+
self.force_replan = self.config.runtime.force_replan
|
|
71
|
+
if self.max_retry is None:
|
|
72
|
+
self.max_retry = self.config.runtime.max_retry
|
|
73
|
+
if self.reset_loops is None:
|
|
74
|
+
self.reset_loops = self.config.runtime.reset_loops
|
|
75
|
+
if self.compare_params is None:
|
|
76
|
+
self.compare_params = self.config.runtime.compare_params
|
|
77
|
+
if self.test_params is None:
|
|
78
|
+
self.test_params = self.config.runtime.test_params
|
|
79
|
+
if self.tool_registry is None:
|
|
80
|
+
self.tool_registry = self.config.tool_registry
|
|
81
|
+
if self.tool_sources is None and self.config.tool_sources is not None:
|
|
82
|
+
self.tool_sources = list(self.config.tool_sources)
|
|
83
|
+
|
|
84
|
+
def _normalize_task_description(self, func: Callable[..., Any]) -> str:
|
|
85
|
+
task_description = inspect.cleandoc(func.__doc__ or "").strip()
|
|
86
|
+
if not task_description:
|
|
87
|
+
raise ValueError("Decorated function must define a docstring to use as task_description.")
|
|
88
|
+
|
|
89
|
+
return task_description
|
|
90
|
+
|
|
91
|
+
def _extract_io_models(self, func: Callable[..., Any]) -> tuple[Type[BaseModel], Type[BaseModel], str]:
|
|
92
|
+
signature = inspect.signature(func)
|
|
93
|
+
params = list(signature.parameters.values())
|
|
94
|
+
if len(params) != 1:
|
|
95
|
+
raise ValueError("Decorated function must accept exactly one parameter annotated with a BaseModel subclass.")
|
|
96
|
+
|
|
97
|
+
input_param = params[0]
|
|
98
|
+
hints = get_type_hints(func)
|
|
99
|
+
input_model = hints.get(input_param.name)
|
|
100
|
+
output_model = hints.get("return")
|
|
101
|
+
|
|
102
|
+
if not inspect.isclass(input_model) or not issubclass(input_model, BaseModel):
|
|
103
|
+
raise TypeError("Decorated function input annotation must be a Pydantic BaseModel subclass.")
|
|
104
|
+
|
|
105
|
+
if not inspect.isclass(output_model) or not issubclass(output_model, BaseModel):
|
|
106
|
+
raise TypeError("Decorated function return annotation must be a Pydantic BaseModel subclass.")
|
|
107
|
+
|
|
108
|
+
return input_model, output_model, input_param.name
|
|
109
|
+
|
|
110
|
+
def _coerce_run_inputs(self, input_model: Type[BaseModel], run_inputs: Any) -> BaseModel:
|
|
111
|
+
if isinstance(run_inputs, input_model):
|
|
112
|
+
return run_inputs
|
|
113
|
+
|
|
114
|
+
if hasattr(input_model, "model_validate"):
|
|
115
|
+
return input_model.model_validate(run_inputs)
|
|
116
|
+
|
|
117
|
+
return input_model.parse_obj(run_inputs)
|
|
118
|
+
|
|
119
|
+
def _run_coro_blocking(self, coro):
|
|
120
|
+
try:
|
|
121
|
+
asyncio.get_running_loop()
|
|
122
|
+
except RuntimeError:
|
|
123
|
+
return asyncio.run(coro)
|
|
124
|
+
|
|
125
|
+
result: Dict[str, Any] = {}
|
|
126
|
+
error: Dict[str, BaseException] = {}
|
|
127
|
+
|
|
128
|
+
def _thread_main():
|
|
129
|
+
try:
|
|
130
|
+
result["value"] = asyncio.run(coro)
|
|
131
|
+
except BaseException as exc: # pragma: no cover
|
|
132
|
+
error["value"] = exc
|
|
133
|
+
|
|
134
|
+
thread = threading.Thread(target=_thread_main, daemon=True)
|
|
135
|
+
thread.start()
|
|
136
|
+
thread.join()
|
|
137
|
+
|
|
138
|
+
if "value" in error:
|
|
139
|
+
raise error["value"]
|
|
140
|
+
|
|
141
|
+
return result["value"]
|
|
142
|
+
|
|
143
|
+
def _resolve_available_tools(self) -> Dict[str, Any]:
|
|
144
|
+
if self.tool_registry is not None and self.tool_sources is not None:
|
|
145
|
+
raise ValueError("Use either tool_registry or tool_sources, not both.")
|
|
146
|
+
|
|
147
|
+
if self.tool_registry is not None:
|
|
148
|
+
return self.tool_registry.build_available_tools()
|
|
149
|
+
|
|
150
|
+
if self.tool_sources is not None:
|
|
151
|
+
return ToolRegistry(sources=self.tool_sources).build_available_tools()
|
|
152
|
+
|
|
153
|
+
if self.available_functions is None or self.available_callables is None:
|
|
154
|
+
raise ValueError(
|
|
155
|
+
"Provide either available_functions and available_callables, or tool_registry/tool_sources."
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
"available_functions": self.available_functions,
|
|
160
|
+
"available_callables": self.available_callables,
|
|
161
|
+
"resolved_tools": None,
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
def as_decorator(self):
|
|
165
|
+
def decorator(func: Callable[..., Any]):
|
|
166
|
+
signature = inspect.signature(func)
|
|
167
|
+
task_description = self._normalize_task_description(func)
|
|
168
|
+
input_model, output_model, input_name = self._extract_io_models(func)
|
|
169
|
+
|
|
170
|
+
async def _invoke_async(run_inputs: Any):
|
|
171
|
+
wa = WorkflowAutoAssembler(
|
|
172
|
+
available_functions=self.resolved_tools["available_functions"],
|
|
173
|
+
available_callables=self.resolved_tools["available_callables"],
|
|
174
|
+
storage_path=self.storage_path,
|
|
175
|
+
llm_handler_params=self.llm_handler_params,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
return await wa.actualize_workflow(
|
|
179
|
+
task_description=task_description,
|
|
180
|
+
force_replan=self.force_replan,
|
|
181
|
+
run_inputs=self._coerce_run_inputs(input_model=input_model, run_inputs=run_inputs),
|
|
182
|
+
test_params=self.test_params,
|
|
183
|
+
compare_params=self.compare_params,
|
|
184
|
+
input_model=input_model,
|
|
185
|
+
output_model=output_model,
|
|
186
|
+
max_retry=self.max_retry,
|
|
187
|
+
reset_loops=self.reset_loops,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if inspect.iscoroutinefunction(func):
|
|
191
|
+
|
|
192
|
+
@wraps(func)
|
|
193
|
+
async def async_wrapper(*args, **kwargs):
|
|
194
|
+
bound = signature.bind(*args, **kwargs)
|
|
195
|
+
return await _invoke_async(bound.arguments[input_name])
|
|
196
|
+
|
|
197
|
+
async_wrapper.input_model = input_model
|
|
198
|
+
async_wrapper.output_model = output_model
|
|
199
|
+
async_wrapper.task_description = task_description
|
|
200
|
+
async_wrapper.ainvoke = _invoke_async
|
|
201
|
+
async_wrapper.resolved_tools = self.resolved_tools["resolved_tools"]
|
|
202
|
+
return async_wrapper
|
|
203
|
+
|
|
204
|
+
@wraps(func)
|
|
205
|
+
def sync_wrapper(*args, **kwargs):
|
|
206
|
+
bound = signature.bind(*args, **kwargs)
|
|
207
|
+
return self._run_coro_blocking(_invoke_async(bound.arguments[input_name]))
|
|
208
|
+
|
|
209
|
+
sync_wrapper.input_model = input_model
|
|
210
|
+
sync_wrapper.output_model = output_model
|
|
211
|
+
sync_wrapper.task_description = task_description
|
|
212
|
+
sync_wrapper.ainvoke = _invoke_async
|
|
213
|
+
sync_wrapper.resolved_tools = self.resolved_tools["resolved_tools"]
|
|
214
|
+
return sync_wrapper
|
|
215
|
+
|
|
216
|
+
return decorator
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def llm_function(
|
|
220
|
+
*,
|
|
221
|
+
available_functions: Optional[List[Any]] = None,
|
|
222
|
+
available_callables: Optional[Dict[str, Callable[..., Any]]] = None,
|
|
223
|
+
tool_registry: Optional[ToolRegistry] = None,
|
|
224
|
+
tool_sources: Optional[List[object]] = None,
|
|
225
|
+
config: Optional[LlmFunctionConfig] = None,
|
|
226
|
+
llm_handler_params: Optional[dict] = None,
|
|
227
|
+
storage_path: Optional[str] = None,
|
|
228
|
+
force_replan: bool = False,
|
|
229
|
+
max_retry: Optional[int] = None,
|
|
230
|
+
reset_loops: Optional[int] = None,
|
|
231
|
+
compare_params: Optional[dict] = None,
|
|
232
|
+
test_params: Optional[list] = None,
|
|
233
|
+
):
|
|
234
|
+
"""
|
|
235
|
+
Decorate a typed function and route its calls through WorkflowAutoAssembler.
|
|
236
|
+
"""
|
|
237
|
+
return LlmFunction(
|
|
238
|
+
available_functions=available_functions,
|
|
239
|
+
available_callables=available_callables,
|
|
240
|
+
tool_registry=tool_registry,
|
|
241
|
+
tool_sources=tool_sources,
|
|
242
|
+
config=config,
|
|
243
|
+
llm_handler_params=llm_handler_params,
|
|
244
|
+
storage_path=storage_path,
|
|
245
|
+
force_replan=force_replan,
|
|
246
|
+
max_retry=max_retry,
|
|
247
|
+
reset_loops=reset_loops,
|
|
248
|
+
compare_params=compare_params,
|
|
249
|
+
test_params=test_params,
|
|
250
|
+
).as_decorator()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
checkpoint[make-package] version=0.0.1 ts=2026-03-29T15:54:16.335632+00:00
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ref: refs/heads/master
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Unnamed repository; edit this file 'description' to name the repository.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
#
|
|
3
|
+
# An example hook script to check the commit log message taken by
|
|
4
|
+
# applypatch from an e-mail message.
|
|
5
|
+
#
|
|
6
|
+
# The hook should exit with non-zero status after issuing an
|
|
7
|
+
# appropriate message if it wants to stop the commit. The hook is
|
|
8
|
+
# allowed to edit the commit message file.
|
|
9
|
+
#
|
|
10
|
+
# To enable this hook, rename this file to "applypatch-msg".
|
|
11
|
+
|
|
12
|
+
. git-sh-setup
|
|
13
|
+
commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
|
|
14
|
+
test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
|
|
15
|
+
:
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
#
|
|
3
|
+
# An example hook script to check the commit log message.
|
|
4
|
+
# Called by "git commit" with one argument, the name of the file
|
|
5
|
+
# that has the commit message. The hook should exit with non-zero
|
|
6
|
+
# status after issuing an appropriate message if it wants to stop the
|
|
7
|
+
# commit. The hook is allowed to edit the commit message file.
|
|
8
|
+
#
|
|
9
|
+
# To enable this hook, rename this file to "commit-msg".
|
|
10
|
+
|
|
11
|
+
# Uncomment the below to add a Signed-off-by line to the message.
|
|
12
|
+
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
|
|
13
|
+
# hook is more suited to it.
|
|
14
|
+
#
|
|
15
|
+
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
|
|
16
|
+
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
|
|
17
|
+
|
|
18
|
+
# This example catches duplicate Signed-off-by lines.
|
|
19
|
+
|
|
20
|
+
test "" = "$(grep '^Signed-off-by: ' "$1" |
|
|
21
|
+
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
|
|
22
|
+
echo >&2 Duplicate Signed-off-by lines.
|
|
23
|
+
exit 1
|
|
24
|
+
}
|