jaclang 0.7.0__py3-none-any.whl → 0.7.2__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 jaclang might be problematic. Click here for more details.
- jaclang/compiler/absyntree.py +53 -50
- jaclang/compiler/compile.py +21 -0
- jaclang/compiler/passes/main/__init__.py +2 -2
- jaclang/compiler/passes/main/def_impl_match_pass.py +10 -8
- jaclang/compiler/passes/main/def_use_pass.py +14 -7
- jaclang/compiler/passes/main/fuse_typeinfo_pass.py +20 -1
- jaclang/compiler/passes/main/import_pass.py +60 -11
- jaclang/compiler/passes/main/pyast_gen_pass.py +65 -4
- jaclang/compiler/passes/main/pyast_load_pass.py +2 -1
- jaclang/compiler/passes/main/pyjac_ast_link_pass.py +6 -1
- jaclang/compiler/passes/main/pyout_pass.py +3 -1
- jaclang/compiler/passes/main/schedules.py +4 -3
- jaclang/compiler/passes/main/tests/fixtures/incautoimpl.jac +7 -0
- jaclang/compiler/passes/main/tests/test_decl_def_match_pass.py +4 -4
- jaclang/compiler/passes/main/tests/test_import_pass.py +21 -0
- jaclang/compiler/passes/main/tests/test_type_check_pass.py +1 -1
- jaclang/compiler/passes/tool/jac_formatter_pass.py +14 -2
- jaclang/compiler/passes/tool/tests/fixtures/doc_string.jac +15 -0
- jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +7 -5
- jaclang/compiler/passes/tool/tests/test_unparse_validate.py +1 -2
- jaclang/compiler/symtable.py +21 -1
- jaclang/core/aott.py +107 -11
- jaclang/core/construct.py +171 -5
- jaclang/core/llms/anthropic.py +31 -2
- jaclang/core/llms/base.py +3 -3
- jaclang/core/llms/groq.py +4 -1
- jaclang/core/llms/huggingface.py +4 -1
- jaclang/core/llms/ollama.py +4 -1
- jaclang/core/llms/openai.py +6 -2
- jaclang/core/llms/togetherai.py +4 -1
- jaclang/langserve/engine.py +193 -121
- jaclang/langserve/server.py +35 -6
- jaclang/langserve/tests/fixtures/circle.jac +73 -0
- jaclang/langserve/tests/fixtures/circle_err.jac +73 -0
- jaclang/langserve/tests/fixtures/circle_pure.impl.jac +32 -0
- jaclang/langserve/tests/fixtures/circle_pure.jac +34 -0
- jaclang/langserve/tests/fixtures/circle_pure_err.impl.jac +32 -0
- jaclang/langserve/tests/fixtures/circle_pure_err.jac +34 -0
- jaclang/langserve/tests/test_server.py +156 -1
- jaclang/langserve/utils.py +127 -2
- jaclang/plugin/default.py +25 -83
- jaclang/plugin/feature.py +10 -12
- jaclang/plugin/tests/test_features.py +0 -33
- jaclang/settings.py +1 -0
- jaclang/tests/fixtures/byllmissue.jac +3 -0
- jaclang/tests/fixtures/hash_init_check.jac +17 -0
- jaclang/tests/fixtures/math_question.jpg +0 -0
- jaclang/tests/fixtures/nosigself.jac +19 -0
- jaclang/tests/fixtures/type_info.jac +1 -1
- jaclang/tests/fixtures/walker_override.jac +21 -0
- jaclang/tests/fixtures/with_llm_vision.jac +25 -0
- jaclang/tests/test_cli.py +1 -1
- jaclang/tests/test_language.py +61 -11
- jaclang/utils/helpers.py +3 -5
- jaclang/utils/test.py +1 -1
- jaclang/utils/treeprinter.py +19 -2
- {jaclang-0.7.0.dist-info → jaclang-0.7.2.dist-info}/METADATA +3 -2
- {jaclang-0.7.0.dist-info → jaclang-0.7.2.dist-info}/RECORD +60 -48
- jaclang/core/memory.py +0 -48
- jaclang/core/shelve_storage.py +0 -55
- {jaclang-0.7.0.dist-info → jaclang-0.7.2.dist-info}/WHEEL +0 -0
- {jaclang-0.7.0.dist-info → jaclang-0.7.2.dist-info}/entry_points.txt +0 -0
|
@@ -127,7 +127,6 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
|
|
|
127
127
|
is_imported=False,
|
|
128
128
|
kid=valid,
|
|
129
129
|
)
|
|
130
|
-
ret.gen.py_ast = [node]
|
|
131
130
|
return self.nu(ret)
|
|
132
131
|
|
|
133
132
|
def proc_function_def(
|
|
@@ -209,6 +208,8 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
|
|
|
209
208
|
kid = ([doc] if doc else []) + (
|
|
210
209
|
[name, sig, valid_body] if sig else [name, valid_body]
|
|
211
210
|
)
|
|
211
|
+
if not sig:
|
|
212
|
+
raise self.ice("Function signature not found")
|
|
212
213
|
ret = ast.Ability(
|
|
213
214
|
name_ref=name,
|
|
214
215
|
is_async=False,
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
"""Jac to python ast link pass.
|
|
1
|
+
"""Jac to python ast link pass.
|
|
2
|
+
|
|
3
|
+
This pass is needed so cases where there are multiple Jac nodes relevant to a
|
|
4
|
+
single python node can be linked. For example FuncDef doesn't have a Name node
|
|
5
|
+
however Ability does.
|
|
6
|
+
"""
|
|
2
7
|
|
|
3
8
|
import ast as ast3
|
|
4
9
|
|
|
@@ -35,7 +35,9 @@ class PyOutPass(Pass):
|
|
|
35
35
|
f"Unable to find module {node.loc.mod_path} or no code present.", node
|
|
36
36
|
)
|
|
37
37
|
return
|
|
38
|
-
mods = [node] +
|
|
38
|
+
mods = [node] + [
|
|
39
|
+
i for i in self.get_all_sub_nodes(node, ast.Module) if not i.stub_only
|
|
40
|
+
]
|
|
39
41
|
for mod in mods:
|
|
40
42
|
mod_path, out_path_py, out_path_pyc = self.get_output_targets(mod)
|
|
41
43
|
if os.path.exists(out_path_pyc) and os.path.getmtime(
|
|
@@ -9,7 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
from .sub_node_tab_pass import SubNodeTabPass # noqa: I100
|
|
10
10
|
from .import_pass import JacImportPass, PyImportPass # noqa: I100
|
|
11
11
|
from .sym_tab_build_pass import SymTabBuildPass # noqa: I100
|
|
12
|
-
from .def_impl_match_pass import
|
|
12
|
+
from .def_impl_match_pass import DeclImplMatchPass # noqa: I100
|
|
13
13
|
from .def_use_pass import DefUsePass # noqa: I100
|
|
14
14
|
from .pyout_pass import PyOutPass # noqa: I100
|
|
15
15
|
from .pybc_gen_pass import PyBytecodeGenPass # noqa: I100
|
|
@@ -25,7 +25,7 @@ py_code_gen = [
|
|
|
25
25
|
JacImportPass,
|
|
26
26
|
PyImportPass,
|
|
27
27
|
SymTabBuildPass,
|
|
28
|
-
|
|
28
|
+
DeclImplMatchPass,
|
|
29
29
|
DefUsePass,
|
|
30
30
|
RegistryPass,
|
|
31
31
|
PyastGenPass,
|
|
@@ -33,5 +33,6 @@ py_code_gen = [
|
|
|
33
33
|
PyBytecodeGenPass,
|
|
34
34
|
]
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
type_checker_sched = [JacTypeCheckPass, FuseTypeInfoPass, AccessCheckPass]
|
|
37
|
+
py_code_gen_typed = [*py_code_gen, *type_checker_sched]
|
|
37
38
|
py_compiler = [*py_code_gen, PyOutPass]
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"""Test pass module."""
|
|
2
2
|
|
|
3
3
|
from jaclang.compiler.compile import jac_file_to_pass
|
|
4
|
-
from jaclang.compiler.passes.main import
|
|
4
|
+
from jaclang.compiler.passes.main import DeclImplMatchPass
|
|
5
5
|
from jaclang.utils.test import TestCase
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class
|
|
8
|
+
class DeclImplMatchPassTests(TestCase):
|
|
9
9
|
"""Test pass module."""
|
|
10
10
|
|
|
11
11
|
def setUp(self) -> None:
|
|
@@ -14,7 +14,7 @@ class DeclDefMatchPassTests(TestCase):
|
|
|
14
14
|
|
|
15
15
|
def test_ability_connected_to_decl(self) -> None:
|
|
16
16
|
"""Basic test for pass."""
|
|
17
|
-
state = jac_file_to_pass(self.fixture_abs_path("base.jac"),
|
|
17
|
+
state = jac_file_to_pass(self.fixture_abs_path("base.jac"), DeclImplMatchPass)
|
|
18
18
|
self.assertFalse(state.errors_had)
|
|
19
19
|
self.assertIn("(o)Test.(c)say_hi", state.ir.sym_tab.tab)
|
|
20
20
|
self.assertIsNotNone(state.ir.sym_tab.tab["(o)Test.(c)say_hi"].decl.body)
|
|
@@ -23,7 +23,7 @@ class DeclDefMatchPassTests(TestCase):
|
|
|
23
23
|
|
|
24
24
|
def test_ability_connected_to_decl_post(self) -> None:
|
|
25
25
|
"""Basic test for pass."""
|
|
26
|
-
state = jac_file_to_pass(self.fixture_abs_path("base2.jac"),
|
|
26
|
+
state = jac_file_to_pass(self.fixture_abs_path("base2.jac"), DeclImplMatchPass)
|
|
27
27
|
self.assertFalse(state.errors_had)
|
|
28
28
|
self.assertIn("(o)Test.(c)say_hi", state.ir.sym_tab.tab)
|
|
29
29
|
self.assertIsNotNone(state.ir.sym_tab.tab["(o)Test.(c)say_hi"].decl.body)
|
|
@@ -28,3 +28,24 @@ class ImportPassPassTests(TestCase):
|
|
|
28
28
|
self.assertIn("getme.impl", mod_names)
|
|
29
29
|
self.assertIn("autoimpl.impl", mod_names)
|
|
30
30
|
self.assertIn("autoimpl.something.else.impl", mod_names)
|
|
31
|
+
|
|
32
|
+
def test_import_include_auto_impl(self) -> None:
|
|
33
|
+
"""Basic test for pass."""
|
|
34
|
+
state = jac_file_to_pass(
|
|
35
|
+
self.fixture_abs_path("incautoimpl.jac"), JacImportPass
|
|
36
|
+
)
|
|
37
|
+
num_modules = len(state.ir.get_all_sub_nodes(ast.Module))
|
|
38
|
+
mod_names = [i.name for i in state.ir.get_all_sub_nodes(ast.Module)]
|
|
39
|
+
self.assertEqual(num_modules, 4)
|
|
40
|
+
self.assertIn("getme.impl", mod_names)
|
|
41
|
+
self.assertIn("autoimpl", mod_names)
|
|
42
|
+
self.assertIn("autoimpl.impl", mod_names)
|
|
43
|
+
self.assertIn("autoimpl.something.else.impl", mod_names)
|
|
44
|
+
|
|
45
|
+
def test_annexalbe_by_discovery(self) -> None:
|
|
46
|
+
"""Basic test for pass."""
|
|
47
|
+
state = jac_file_to_pass(
|
|
48
|
+
self.fixture_abs_path("incautoimpl.jac"), JacImportPass
|
|
49
|
+
)
|
|
50
|
+
for i in state.ir.get_all_sub_nodes(ast.Module):
|
|
51
|
+
self.assertEqual(i.annexable_by, self.fixture_abs_path("autoimpl.jac"))
|
|
@@ -59,6 +59,6 @@ class MypyTypeCheckPassTests(TestCase):
|
|
|
59
59
|
self.assertIn("HasVar - species - Type: builtins.str", out)
|
|
60
60
|
self.assertIn("myDog - Type: type_info.Dog", out)
|
|
61
61
|
self.assertIn("Body - Type: type_info.Dog.Body", out)
|
|
62
|
-
self.assertEqual(out.count("Type: builtins.str"),
|
|
62
|
+
self.assertEqual(out.count("Type: builtins.str"), 28)
|
|
63
63
|
for i in lis:
|
|
64
64
|
self.assertNotIn(i, out)
|
|
@@ -421,7 +421,10 @@ class JacFormatPass(Pass):
|
|
|
421
421
|
self.emit_ln(node, "")
|
|
422
422
|
self.emit_ln(node, i.gen.jac)
|
|
423
423
|
if isinstance(i, ast.Token) and i.name == Tok.KW_BY:
|
|
424
|
-
|
|
424
|
+
if not node.params:
|
|
425
|
+
self.emit(node, f"{i.gen.jac} ")
|
|
426
|
+
else:
|
|
427
|
+
self.emit(node, f" {i.gen.jac} ")
|
|
425
428
|
else:
|
|
426
429
|
if (
|
|
427
430
|
line_break_needed
|
|
@@ -2402,7 +2405,16 @@ class JacFormatPass(Pass):
|
|
|
2402
2405
|
and isinstance(node.parent.parent, ast.FString)
|
|
2403
2406
|
):
|
|
2404
2407
|
self.emit(node, node.value)
|
|
2405
|
-
|
|
2408
|
+
if "\n" in node.value:
|
|
2409
|
+
string_type = node.value[0:3]
|
|
2410
|
+
pure_string = node.value[3:-3]
|
|
2411
|
+
lines = pure_string.split("\n")
|
|
2412
|
+
self.emit(node, string_type)
|
|
2413
|
+
for line in lines[:-1]:
|
|
2414
|
+
self.emit_ln(node, line)
|
|
2415
|
+
self.emit_ln(node, f"{lines[-1]}{string_type}")
|
|
2416
|
+
else:
|
|
2417
|
+
self.emit(node, node.value)
|
|
2406
2418
|
|
|
2407
2419
|
def enter_bool(self, node: ast.Bool) -> None:
|
|
2408
2420
|
"""Sub objects.
|
|
@@ -44,8 +44,8 @@ class JacFormatPassTests(TestCaseMicroSuite, AstSyncTestMixin):
|
|
|
44
44
|
|
|
45
45
|
if diff:
|
|
46
46
|
print(f"Differences found in comparison:\n{diff}")
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
raise AssertionError("Files differ after formatting.")
|
|
48
|
+
|
|
49
49
|
except FileNotFoundError:
|
|
50
50
|
print(f"File not found: {original_file} or {formatted_file}")
|
|
51
51
|
raise
|
|
@@ -76,6 +76,9 @@ class JacFormatPassTests(TestCaseMicroSuite, AstSyncTestMixin):
|
|
|
76
76
|
self.compare_files(
|
|
77
77
|
os.path.join(self.fixture_abs_path(""), "corelib_fmt.jac"),
|
|
78
78
|
)
|
|
79
|
+
self.compare_files(
|
|
80
|
+
os.path.join(self.fixture_abs_path(""), "doc_string.jac"),
|
|
81
|
+
)
|
|
79
82
|
|
|
80
83
|
def test_compare_myca_fixtures(self) -> None:
|
|
81
84
|
"""Tests if files in the myca fixtures directory do not change after being formatted."""
|
|
@@ -135,14 +138,13 @@ class JacFormatPassTests(TestCaseMicroSuite, AstSyncTestMixin):
|
|
|
135
138
|
diff = "\n".join(unified_diff(before.splitlines(), after.splitlines()))
|
|
136
139
|
self.assertFalse(diff, "AST structures differ after formatting.")
|
|
137
140
|
|
|
138
|
-
except Exception:
|
|
141
|
+
except Exception as e:
|
|
139
142
|
print(add_line_numbers(code_gen_pure.ir.source.code))
|
|
140
143
|
print("\n+++++++++++++++++++++++++++++++++++++++\n")
|
|
141
144
|
print(add_line_numbers(code_gen_format.ir.gen.jac))
|
|
142
145
|
print("\n+++++++++++++++++++++++++++++++++++++++\n")
|
|
143
146
|
print("\n".join(unified_diff(before.splitlines(), after.splitlines())))
|
|
144
|
-
|
|
145
|
-
# raise e
|
|
147
|
+
raise e
|
|
146
148
|
|
|
147
149
|
|
|
148
150
|
JacFormatPassTests.self_attach_micro_tests()
|
|
@@ -30,7 +30,7 @@ class JacUnparseTests(TestCaseMicroSuite, AstSyncTestMixin):
|
|
|
30
30
|
self.assertEqual(x, y)
|
|
31
31
|
except Exception as e:
|
|
32
32
|
print("\n".join(unified_diff(x.splitlines(), y.splitlines())))
|
|
33
|
-
|
|
33
|
+
raise e
|
|
34
34
|
|
|
35
35
|
def micro_suite_test(self, filename: str) -> None:
|
|
36
36
|
"""Parse micro jac file."""
|
|
@@ -75,7 +75,6 @@ class JacUnparseTests(TestCaseMicroSuite, AstSyncTestMixin):
|
|
|
75
75
|
|
|
76
76
|
except Exception as e:
|
|
77
77
|
raise e
|
|
78
|
-
# self.skipTest(f"Test failed, but skipping instead of failing: {e}")
|
|
79
78
|
|
|
80
79
|
|
|
81
80
|
JacUnparseTests.self_attach_micro_tests()
|
jaclang/compiler/symtable.py
CHANGED
|
@@ -17,7 +17,7 @@ class SymbolType(Enum):
|
|
|
17
17
|
|
|
18
18
|
MODULE = "module" # LSP: Module
|
|
19
19
|
MOD_VAR = "mod_var" # LSP: Variable
|
|
20
|
-
VAR = "
|
|
20
|
+
VAR = "variable" # LSP: Variable
|
|
21
21
|
IMM_VAR = "immutable" # LSP: Constant
|
|
22
22
|
ABILITY = "ability" # LSP: Function
|
|
23
23
|
OBJECT_ARCH = "object" # LSP: Class
|
|
@@ -54,6 +54,12 @@ class SymbolInfo:
|
|
|
54
54
|
self.acc_tag: Optional[SymbolAccess] = acc_tag
|
|
55
55
|
self.typ_sym_table: Optional[SymbolTable] = None
|
|
56
56
|
|
|
57
|
+
@property
|
|
58
|
+
def clean_type(self) -> str:
|
|
59
|
+
"""Get clean type."""
|
|
60
|
+
ret_type = self.typ.replace("builtins.", "").replace("NoType", "")
|
|
61
|
+
return ret_type
|
|
62
|
+
|
|
57
63
|
|
|
58
64
|
class SymbolAccess(Enum):
|
|
59
65
|
"""Symbol types."""
|
|
@@ -101,6 +107,20 @@ class Symbol:
|
|
|
101
107
|
"""Get sym_type."""
|
|
102
108
|
return self.decl.sym_type
|
|
103
109
|
|
|
110
|
+
@property
|
|
111
|
+
def sym_path_str(self) -> str:
|
|
112
|
+
"""Return a full path of the symbol."""
|
|
113
|
+
out = [self.defn[0].sym_name]
|
|
114
|
+
current_tab = self.parent_tab
|
|
115
|
+
while current_tab is not None:
|
|
116
|
+
out.append(current_tab.name)
|
|
117
|
+
if current_tab.has_parent():
|
|
118
|
+
current_tab = current_tab.parent
|
|
119
|
+
else:
|
|
120
|
+
break
|
|
121
|
+
out.reverse()
|
|
122
|
+
return ".".join(out)
|
|
123
|
+
|
|
104
124
|
def add_defn(self, node: ast.AstSymbolNode) -> None:
|
|
105
125
|
"""Add defn."""
|
|
106
126
|
self.defn.append(node)
|
jaclang/core/aott.py
CHANGED
|
@@ -4,18 +4,30 @@ AOTT: Automated Operational Type Transformation.
|
|
|
4
4
|
This has all the necessary functions to perform the AOTT operations.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import base64
|
|
8
|
+
import logging
|
|
7
9
|
import re
|
|
8
10
|
from enum import Enum
|
|
11
|
+
from io import BytesIO
|
|
9
12
|
from typing import Any
|
|
10
13
|
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
from PIL import Image
|
|
17
|
+
except ImportError:
|
|
18
|
+
Image = None
|
|
19
|
+
|
|
11
20
|
from jaclang.core.llms.base import BaseLLM
|
|
12
21
|
from jaclang.core.registry import SemInfo, SemRegistry, SemScope
|
|
13
22
|
|
|
14
23
|
|
|
24
|
+
IMG_FORMATS = ["PngImageFile", "JpegImageFile"]
|
|
25
|
+
|
|
26
|
+
|
|
15
27
|
def aott_raise(
|
|
16
28
|
model: BaseLLM,
|
|
17
29
|
information: str,
|
|
18
|
-
inputs_information: str,
|
|
30
|
+
inputs_information: str | list[dict],
|
|
19
31
|
output_information: str,
|
|
20
32
|
type_explanations: str,
|
|
21
33
|
action: str,
|
|
@@ -25,18 +37,43 @@ def aott_raise(
|
|
|
25
37
|
model_params: dict,
|
|
26
38
|
) -> str:
|
|
27
39
|
"""AOTT Raise uses the information (Meanings types values) provided to generate a prompt(meaning in)."""
|
|
40
|
+
system_prompt = model.MTLLM_SYSTEM_PROMPT
|
|
41
|
+
meaning_in: str | list[dict]
|
|
28
42
|
if method != "ReAct":
|
|
29
|
-
system_prompt = model.MTLLM_SYSTEM_PROMPT
|
|
30
|
-
mtllm_prompt = model.MTLLM_PROMPT.format(
|
|
31
|
-
information=information,
|
|
32
|
-
inputs_information=inputs_information,
|
|
33
|
-
output_information=output_information,
|
|
34
|
-
type_explanations=type_explanations,
|
|
35
|
-
action=action,
|
|
36
|
-
context=context,
|
|
37
|
-
)
|
|
38
43
|
method_prompt = model.MTLLM_METHOD_PROMPTS[method]
|
|
39
|
-
|
|
44
|
+
if isinstance(inputs_information, str):
|
|
45
|
+
mtllm_prompt = model.MTLLM_PROMPT.format(
|
|
46
|
+
information=information,
|
|
47
|
+
inputs_information=inputs_information,
|
|
48
|
+
output_information=output_information,
|
|
49
|
+
type_explanations=type_explanations,
|
|
50
|
+
action=action,
|
|
51
|
+
context=context,
|
|
52
|
+
).strip()
|
|
53
|
+
meaning_in = f"{system_prompt}\n{mtllm_prompt}\n{method_prompt}".strip()
|
|
54
|
+
else:
|
|
55
|
+
upper_half = model.MTLLM_PROMPT.split("{inputs_information}")[0]
|
|
56
|
+
lower_half = model.MTLLM_PROMPT.split("{inputs_information}")[1]
|
|
57
|
+
upper_half = upper_half.format(
|
|
58
|
+
information=information,
|
|
59
|
+
context=context,
|
|
60
|
+
)
|
|
61
|
+
lower_half = lower_half.format(
|
|
62
|
+
output_information=output_information,
|
|
63
|
+
type_explanations=type_explanations,
|
|
64
|
+
action=action,
|
|
65
|
+
)
|
|
66
|
+
meaning_in = (
|
|
67
|
+
[
|
|
68
|
+
{"type": "text", "text": system_prompt},
|
|
69
|
+
{"type": "text", "text": upper_half},
|
|
70
|
+
]
|
|
71
|
+
+ inputs_information
|
|
72
|
+
+ [
|
|
73
|
+
{"type": "text", "text": lower_half},
|
|
74
|
+
{"type": "text", "text": method_prompt},
|
|
75
|
+
]
|
|
76
|
+
)
|
|
40
77
|
return model(meaning_in, **model_params)
|
|
41
78
|
else:
|
|
42
79
|
assert tools, "Tools must be provided for the ReAct method."
|
|
@@ -212,3 +249,62 @@ class Tool:
|
|
|
212
249
|
"""Initialize the Tool class."""
|
|
213
250
|
# TODO: Implement the Tool class
|
|
214
251
|
pass
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def get_input_information(
|
|
255
|
+
inputs: list[tuple[str, str, str, Any]], type_collector: list
|
|
256
|
+
) -> str | list[dict]:
|
|
257
|
+
"""
|
|
258
|
+
Get the input information for the AOTT operation.
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
str | list[dict]: If the input does not contain images, returns a string with the input information.
|
|
262
|
+
If the input contains images, returns a list of dictionaries representing the input information,
|
|
263
|
+
where each dictionary contains either text or image_url.
|
|
264
|
+
|
|
265
|
+
"""
|
|
266
|
+
contains_imgs = any(get_type_annotation(i[3]) in IMG_FORMATS for i in inputs)
|
|
267
|
+
if not contains_imgs:
|
|
268
|
+
inputs_information_list = []
|
|
269
|
+
for i in inputs:
|
|
270
|
+
typ_anno = get_type_annotation(i[3])
|
|
271
|
+
type_collector.extend(extract_non_primary_type(typ_anno))
|
|
272
|
+
inputs_information_list.append(
|
|
273
|
+
f"{i[0]} ({i[2]}) ({typ_anno}) = {get_object_string(i[3])}"
|
|
274
|
+
)
|
|
275
|
+
return "\n".join(inputs_information_list)
|
|
276
|
+
else:
|
|
277
|
+
inputs_information_dict_list: list[dict] = []
|
|
278
|
+
for i in inputs:
|
|
279
|
+
if get_type_annotation(i[3]) in IMG_FORMATS:
|
|
280
|
+
img_base64 = image_to_base64(i[3])
|
|
281
|
+
image_repr: list[dict] = [
|
|
282
|
+
{
|
|
283
|
+
"type": "text",
|
|
284
|
+
"text": f"{i[0]} ({i[2]}) (Image) = ",
|
|
285
|
+
},
|
|
286
|
+
{"type": "image_url", "image_url": {"url": img_base64}},
|
|
287
|
+
]
|
|
288
|
+
inputs_information_dict_list.extend(image_repr)
|
|
289
|
+
continue
|
|
290
|
+
typ_anno = get_type_annotation(i[3])
|
|
291
|
+
type_collector.extend(extract_non_primary_type(typ_anno))
|
|
292
|
+
inputs_information_dict_list.append(
|
|
293
|
+
{
|
|
294
|
+
"type": "text",
|
|
295
|
+
"text": f"{i[0]} ({i[2]}) ({typ_anno}) = {get_object_string(i[3])}",
|
|
296
|
+
}
|
|
297
|
+
)
|
|
298
|
+
return inputs_information_dict_list
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def image_to_base64(image: Image) -> str:
|
|
302
|
+
"""Convert an image to base64 expected by OpenAI."""
|
|
303
|
+
if not Image:
|
|
304
|
+
log = logging.getLogger(__name__)
|
|
305
|
+
log.error("Pillow is not installed. Please install Pillow to use images.")
|
|
306
|
+
return ""
|
|
307
|
+
img_format = image.format
|
|
308
|
+
with BytesIO() as buffer:
|
|
309
|
+
image.save(buffer, format=img_format, quality=100)
|
|
310
|
+
return f"data:image/{img_format.lower()};base64,{base64.b64encode(buffer.getvalue()).decode()}"
|
jaclang/core/construct.py
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import shelve
|
|
5
6
|
import unittest
|
|
7
|
+
from contextvars import ContextVar
|
|
6
8
|
from dataclasses import dataclass, field
|
|
7
9
|
from typing import Callable, Optional
|
|
8
10
|
from uuid import UUID, uuid4
|
|
9
11
|
|
|
10
12
|
from jaclang.compiler.constant import EdgeDir
|
|
11
13
|
from jaclang.core.utils import collect_node_connections
|
|
12
|
-
from jaclang.plugin.feature import JacFeature as Jac
|
|
13
14
|
from jaclang.plugin.spec import DSFunc
|
|
14
15
|
|
|
15
16
|
|
|
@@ -57,6 +58,8 @@ class NodeAnchor(ObjectAnchor):
|
|
|
57
58
|
|
|
58
59
|
def populate_edges(self) -> None:
|
|
59
60
|
"""Populate edges from edge ids."""
|
|
61
|
+
from jaclang.plugin.feature import JacFeature as Jac
|
|
62
|
+
|
|
60
63
|
if len(self.edges) == 0 and len(self.edge_ids) > 0:
|
|
61
64
|
for e_id in self.edge_ids:
|
|
62
65
|
edge = Jac.context().get_obj(e_id)
|
|
@@ -354,11 +357,15 @@ class NodeArchitype(Architype):
|
|
|
354
357
|
|
|
355
358
|
def __init__(self) -> None:
|
|
356
359
|
"""Create node architype."""
|
|
360
|
+
from jaclang.plugin.feature import JacFeature as Jac
|
|
361
|
+
|
|
357
362
|
self._jac_: NodeAnchor = NodeAnchor(obj=self)
|
|
358
363
|
Jac.context().save_obj(self, persistent=self._jac_.persistent)
|
|
359
364
|
|
|
360
365
|
def save(self) -> None:
|
|
361
366
|
"""Save the node to the memory/storage hierarchy."""
|
|
367
|
+
from jaclang.plugin.feature import JacFeature as Jac
|
|
368
|
+
|
|
362
369
|
self._jac_.persistent = True
|
|
363
370
|
Jac.context().save_obj(self, persistent=True)
|
|
364
371
|
|
|
@@ -383,11 +390,15 @@ class EdgeArchitype(Architype):
|
|
|
383
390
|
|
|
384
391
|
def __init__(self) -> None:
|
|
385
392
|
"""Create edge architype."""
|
|
393
|
+
from jaclang.plugin.feature import JacFeature as Jac
|
|
394
|
+
|
|
386
395
|
self._jac_: EdgeAnchor = EdgeAnchor(obj=self)
|
|
387
396
|
Jac.context().save_obj(self, persistent=self.persistent)
|
|
388
397
|
|
|
389
398
|
def save(self) -> None:
|
|
390
399
|
"""Save the edge to the memory/storage hierarchy."""
|
|
400
|
+
from jaclang.plugin.feature import JacFeature as Jac
|
|
401
|
+
|
|
391
402
|
self.persistent = True
|
|
392
403
|
Jac.context().save_obj(self, persistent=True)
|
|
393
404
|
|
|
@@ -405,6 +416,8 @@ class EdgeArchitype(Architype):
|
|
|
405
416
|
|
|
406
417
|
def populate_nodes(self) -> None:
|
|
407
418
|
"""Populate nodes for the edges from node ids."""
|
|
419
|
+
from jaclang.plugin.feature import JacFeature as Jac
|
|
420
|
+
|
|
408
421
|
if self._jac_.source_id:
|
|
409
422
|
obj = Jac.context().get_obj(self._jac_.source_id)
|
|
410
423
|
if obj is None:
|
|
@@ -439,6 +452,13 @@ class WalkerArchitype(Architype):
|
|
|
439
452
|
self._jac_: WalkerAnchor = WalkerAnchor(obj=self)
|
|
440
453
|
|
|
441
454
|
|
|
455
|
+
class GenericEdge(EdgeArchitype):
|
|
456
|
+
"""Generic Root Node."""
|
|
457
|
+
|
|
458
|
+
_jac_entry_funcs_ = []
|
|
459
|
+
_jac_exit_funcs_ = []
|
|
460
|
+
|
|
461
|
+
|
|
442
462
|
class Root(NodeArchitype):
|
|
443
463
|
"""Generic Root Node."""
|
|
444
464
|
|
|
@@ -460,11 +480,157 @@ class Root(NodeArchitype):
|
|
|
460
480
|
self._jac_.edges = []
|
|
461
481
|
|
|
462
482
|
|
|
463
|
-
class
|
|
464
|
-
"""
|
|
483
|
+
class Memory:
|
|
484
|
+
"""Memory module interface."""
|
|
465
485
|
|
|
466
|
-
|
|
467
|
-
|
|
486
|
+
mem: dict[UUID, Architype] = {}
|
|
487
|
+
save_obj_list: dict[UUID, Architype] = {}
|
|
488
|
+
|
|
489
|
+
def __init__(self) -> None:
|
|
490
|
+
"""init."""
|
|
491
|
+
pass
|
|
492
|
+
|
|
493
|
+
def get_obj(self, obj_id: UUID) -> Architype | None:
|
|
494
|
+
"""Get object from memory."""
|
|
495
|
+
return self.get_obj_from_store(obj_id)
|
|
496
|
+
|
|
497
|
+
def get_obj_from_store(self, obj_id: UUID) -> Architype | None:
|
|
498
|
+
"""Get object from the underlying store."""
|
|
499
|
+
ret = self.mem.get(obj_id)
|
|
500
|
+
return ret
|
|
501
|
+
|
|
502
|
+
def has_obj(self, obj_id: UUID) -> bool:
|
|
503
|
+
"""Check if the object exists."""
|
|
504
|
+
return self.has_obj_in_store(obj_id)
|
|
505
|
+
|
|
506
|
+
def has_obj_in_store(self, obj_id: UUID) -> bool:
|
|
507
|
+
"""Check if the object exists in the underlying store."""
|
|
508
|
+
return obj_id in self.mem
|
|
509
|
+
|
|
510
|
+
def save_obj(self, item: Architype, persistent: bool) -> None:
|
|
511
|
+
"""Save object."""
|
|
512
|
+
self.mem[item._jac_.id] = item
|
|
513
|
+
if persistent:
|
|
514
|
+
# TODO: check if it needs to be saved, i.e. dirty or not
|
|
515
|
+
self.save_obj_list[item._jac_.id] = item
|
|
516
|
+
|
|
517
|
+
def commit(self) -> None:
|
|
518
|
+
"""Commit changes to persistent storage, if applicable."""
|
|
519
|
+
pass
|
|
520
|
+
|
|
521
|
+
def close(self) -> None:
|
|
522
|
+
"""Close any connection, if applicable."""
|
|
523
|
+
self.mem.clear()
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
class ShelveStorage(Memory):
|
|
527
|
+
"""Shelve storage for jaclang runtime object."""
|
|
528
|
+
|
|
529
|
+
storage: shelve.Shelf | None = None
|
|
530
|
+
|
|
531
|
+
def __init__(self, session: str = "") -> None:
|
|
532
|
+
"""Init shelve storage."""
|
|
533
|
+
super().__init__()
|
|
534
|
+
if session:
|
|
535
|
+
self.connect(session)
|
|
536
|
+
|
|
537
|
+
def get_obj_from_store(self, obj_id: UUID) -> Architype | None:
|
|
538
|
+
"""Get object from the underlying store."""
|
|
539
|
+
obj = super().get_obj_from_store(obj_id)
|
|
540
|
+
if obj is None and self.storage:
|
|
541
|
+
obj = self.storage.get(str(obj_id))
|
|
542
|
+
if obj is not None:
|
|
543
|
+
self.mem[obj_id] = obj
|
|
544
|
+
|
|
545
|
+
return obj
|
|
546
|
+
|
|
547
|
+
def has_obj_in_store(self, obj_id: UUID | str) -> bool:
|
|
548
|
+
"""Check if the object exists in the underlying store."""
|
|
549
|
+
return obj_id in self.mem or (
|
|
550
|
+
str(obj_id) in self.storage if self.storage else False
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
def commit(self) -> None:
|
|
554
|
+
"""Commit changes to persistent storage."""
|
|
555
|
+
if self.storage is not None:
|
|
556
|
+
for obj_id, obj in self.save_obj_list.items():
|
|
557
|
+
self.storage[str(obj_id)] = obj
|
|
558
|
+
self.save_obj_list.clear()
|
|
559
|
+
|
|
560
|
+
def connect(self, session: str) -> None:
|
|
561
|
+
"""Connect to storage."""
|
|
562
|
+
self.session = session
|
|
563
|
+
self.storage = shelve.open(session)
|
|
564
|
+
|
|
565
|
+
def close(self) -> None:
|
|
566
|
+
"""Close the storage."""
|
|
567
|
+
super().close()
|
|
568
|
+
self.commit()
|
|
569
|
+
if self.storage:
|
|
570
|
+
self.storage.close()
|
|
571
|
+
self.storage = None
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
class ExecutionContext:
|
|
575
|
+
"""Default Execution Context implementation."""
|
|
576
|
+
|
|
577
|
+
mem: Optional[Memory]
|
|
578
|
+
root: Optional[Root]
|
|
579
|
+
|
|
580
|
+
def __init__(self) -> None:
|
|
581
|
+
"""Create execution context."""
|
|
582
|
+
super().__init__()
|
|
583
|
+
self.mem = ShelveStorage()
|
|
584
|
+
self.root = None
|
|
585
|
+
|
|
586
|
+
def init_memory(self, session: str = "") -> None:
|
|
587
|
+
"""Initialize memory."""
|
|
588
|
+
if session:
|
|
589
|
+
self.mem = ShelveStorage(session)
|
|
590
|
+
else:
|
|
591
|
+
self.mem = Memory()
|
|
592
|
+
|
|
593
|
+
def get_root(self) -> Root:
|
|
594
|
+
"""Get the root object."""
|
|
595
|
+
if self.mem is None:
|
|
596
|
+
raise ValueError("Memory not initialized")
|
|
597
|
+
|
|
598
|
+
if not self.root:
|
|
599
|
+
root = self.mem.get_obj(UUID(int=0))
|
|
600
|
+
if root is None:
|
|
601
|
+
self.root = Root()
|
|
602
|
+
self.mem.save_obj(self.root, persistent=self.root._jac_.persistent)
|
|
603
|
+
elif not isinstance(root, Root):
|
|
604
|
+
raise ValueError(f"Invalid root object: {root}")
|
|
605
|
+
else:
|
|
606
|
+
self.root = root
|
|
607
|
+
return self.root
|
|
608
|
+
|
|
609
|
+
def get_obj(self, obj_id: UUID) -> Architype | None:
|
|
610
|
+
"""Get object from memory."""
|
|
611
|
+
if self.mem is None:
|
|
612
|
+
raise ValueError("Memory not initialized")
|
|
613
|
+
|
|
614
|
+
return self.mem.get_obj(obj_id)
|
|
615
|
+
|
|
616
|
+
def save_obj(self, item: Architype, persistent: bool) -> None:
|
|
617
|
+
"""Save object to memory."""
|
|
618
|
+
if self.mem is None:
|
|
619
|
+
raise ValueError("Memory not initialized")
|
|
620
|
+
|
|
621
|
+
self.mem.save_obj(item, persistent)
|
|
622
|
+
|
|
623
|
+
def reset(self) -> None:
|
|
624
|
+
"""Reset the execution context."""
|
|
625
|
+
if self.mem:
|
|
626
|
+
self.mem.close()
|
|
627
|
+
self.mem = None
|
|
628
|
+
self.root = None
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
exec_context: ContextVar[ExecutionContext | None] = ContextVar(
|
|
632
|
+
"ExecutionContext", default=None
|
|
633
|
+
)
|
|
468
634
|
|
|
469
635
|
|
|
470
636
|
class JacTestResult(unittest.TextTestResult):
|