lionagi 0.1.0__py3-none-any.whl → 0.1.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.
Files changed (83) hide show
  1. lionagi/core/agent/base_agent.py +2 -3
  2. lionagi/core/branch/base.py +1 -1
  3. lionagi/core/branch/branch.py +2 -1
  4. lionagi/core/branch/flow_mixin.py +1 -1
  5. lionagi/core/branch/util.py +1 -1
  6. lionagi/core/execute/base_executor.py +1 -4
  7. lionagi/core/execute/branch_executor.py +66 -3
  8. lionagi/core/execute/instruction_map_executor.py +48 -0
  9. lionagi/core/execute/neo4j_executor.py +381 -0
  10. lionagi/core/execute/structure_executor.py +99 -3
  11. lionagi/core/flow/monoflow/ReAct.py +18 -18
  12. lionagi/core/flow/monoflow/chat_mixin.py +1 -1
  13. lionagi/core/flow/monoflow/followup.py +11 -12
  14. lionagi/core/flow/polyflow/__init__.py +1 -1
  15. lionagi/core/generic/component.py +0 -2
  16. lionagi/core/generic/condition.py +1 -1
  17. lionagi/core/generic/edge.py +52 -0
  18. lionagi/core/mail/mail_manager.py +3 -2
  19. lionagi/core/session/session.py +1 -1
  20. lionagi/experimental/__init__.py +0 -0
  21. lionagi/experimental/directive/__init__.py +0 -0
  22. lionagi/experimental/directive/evaluator/__init__.py +0 -0
  23. lionagi/experimental/directive/evaluator/ast_evaluator.py +115 -0
  24. lionagi/experimental/directive/evaluator/base_evaluator.py +202 -0
  25. lionagi/experimental/directive/evaluator/sandbox_.py +14 -0
  26. lionagi/experimental/directive/evaluator/script_engine.py +83 -0
  27. lionagi/experimental/directive/parser/__init__.py +0 -0
  28. lionagi/experimental/directive/parser/base_parser.py +215 -0
  29. lionagi/experimental/directive/schema.py +36 -0
  30. lionagi/experimental/directive/template_/__init__.py +0 -0
  31. lionagi/experimental/directive/template_/base_template.py +63 -0
  32. lionagi/experimental/tool/__init__.py +0 -0
  33. lionagi/experimental/tool/function_calling.py +43 -0
  34. lionagi/experimental/tool/manual.py +66 -0
  35. lionagi/experimental/tool/schema.py +59 -0
  36. lionagi/experimental/tool/tool_manager.py +138 -0
  37. lionagi/experimental/tool/util.py +16 -0
  38. lionagi/experimental/work/__init__.py +0 -0
  39. lionagi/experimental/work/_logger.py +25 -0
  40. lionagi/experimental/work/exchange.py +0 -0
  41. lionagi/experimental/work/schema.py +30 -0
  42. lionagi/experimental/work/tests.py +72 -0
  43. lionagi/experimental/work/util.py +0 -0
  44. lionagi/experimental/work/work_function.py +89 -0
  45. lionagi/experimental/work/worker.py +12 -0
  46. lionagi/integrations/bridge/autogen_/__init__.py +0 -0
  47. lionagi/integrations/bridge/autogen_/autogen_.py +124 -0
  48. lionagi/integrations/bridge/llamaindex_/get_index.py +294 -0
  49. lionagi/integrations/bridge/llamaindex_/llama_pack.py +227 -0
  50. lionagi/integrations/bridge/transformers_/__init__.py +0 -0
  51. lionagi/integrations/bridge/transformers_/install_.py +36 -0
  52. lionagi/integrations/config/oai_configs.py +1 -1
  53. lionagi/integrations/config/ollama_configs.py +1 -1
  54. lionagi/integrations/config/openrouter_configs.py +1 -1
  55. lionagi/integrations/storage/__init__.py +3 -0
  56. lionagi/integrations/storage/neo4j.py +673 -0
  57. lionagi/integrations/storage/storage_util.py +289 -0
  58. lionagi/integrations/storage/to_csv.py +63 -0
  59. lionagi/integrations/storage/to_excel.py +67 -0
  60. lionagi/libs/ln_knowledge_graph.py +405 -0
  61. lionagi/libs/ln_queue.py +101 -0
  62. lionagi/libs/ln_tokenizer.py +57 -0
  63. lionagi/libs/sys_util.py +1 -1
  64. lionagi/lions/__init__.py +0 -0
  65. lionagi/lions/coder/__init__.py +0 -0
  66. lionagi/lions/coder/add_feature.py +20 -0
  67. lionagi/lions/coder/base_prompts.py +22 -0
  68. lionagi/lions/coder/coder.py +121 -0
  69. lionagi/lions/coder/util.py +91 -0
  70. lionagi/lions/researcher/__init__.py +0 -0
  71. lionagi/lions/researcher/data_source/__init__.py +0 -0
  72. lionagi/lions/researcher/data_source/finhub_.py +191 -0
  73. lionagi/lions/researcher/data_source/google_.py +199 -0
  74. lionagi/lions/researcher/data_source/wiki_.py +96 -0
  75. lionagi/lions/researcher/data_source/yfinance_.py +21 -0
  76. lionagi/tests/libs/test_queue.py +67 -0
  77. lionagi/tests/test_core/test_branch.py +0 -1
  78. lionagi/version.py +1 -1
  79. {lionagi-0.1.0.dist-info → lionagi-0.1.1.dist-info}/METADATA +1 -1
  80. {lionagi-0.1.0.dist-info → lionagi-0.1.1.dist-info}/RECORD +83 -29
  81. {lionagi-0.1.0.dist-info → lionagi-0.1.1.dist-info}/LICENSE +0 -0
  82. {lionagi-0.1.0.dist-info → lionagi-0.1.1.dist-info}/WHEEL +0 -0
  83. {lionagi-0.1.0.dist-info → lionagi-0.1.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,66 @@
1
+ import re
2
+ from typing import Dict, Union, Callable, Any
3
+
4
+
5
+ class BaseManual:
6
+ def __init__(self, template_str: str):
7
+ self.template_str = template_str
8
+
9
+ def _evaluate_condition(self, match, context):
10
+ condition, text = match.groups()
11
+ # Future implementations might parse and evaluate the condition more thoroughly
12
+ return text if condition in context and context[condition] else ""
13
+
14
+ def _render_conditionals(self, context: Dict[str, Union[str, int, float]]) -> str:
15
+ conditional_pattern = re.compile(r"\{if (.*?)\}(.*?)\{endif\}", re.DOTALL)
16
+ return conditional_pattern.sub(
17
+ lambda match: self._evaluate_condition(match, context), self.template_str
18
+ )
19
+
20
+ def _replace_callable(self, match, context):
21
+ key = match.group(1)
22
+ if key in context:
23
+ value = context[key]
24
+ return str(value() if callable(value) else value)
25
+ return match.group(0) # Unmatched placeholders remain unchanged.
26
+
27
+ def _render_placeholders(
28
+ self,
29
+ rendered_template: str,
30
+ context: Dict[str, Union[str, int, float, Callable]],
31
+ ) -> str:
32
+ return re.sub(
33
+ r"\{(\w+)\}",
34
+ lambda match: self._replace_callable(match, context),
35
+ rendered_template,
36
+ )
37
+
38
+ def generate(self, context: Dict[str, Union[str, int, float, Callable]]) -> str:
39
+ """
40
+ Generates output by first processing conditionals, then rendering placeholders,
41
+ including executing callable objects for dynamic data generation.
42
+ """
43
+ template_with_conditionals = self._render_conditionals(context)
44
+ final_output = self._render_placeholders(template_with_conditionals, context)
45
+ return final_output
46
+
47
+
48
+ # from experiments.executor.executor import SafeEvaluator
49
+
50
+ # class DecisionTreeManual:
51
+ # def __init__(self, root):
52
+ # self.root = root
53
+ # self.evaluator = SafeEvaluator()
54
+
55
+ # def evaluate(self, context):
56
+ # return self._traverse_tree(self.root, context)
57
+
58
+ # def _traverse_tree(self, node, context):
59
+ # if isinstance(node, CompositeActionNode) or isinstance(node, ActionNode):
60
+ # return node.execute(context)
61
+ # elif isinstance(node, DecisionNode):
62
+ # condition_result = self.evaluator.evaluate(node.condition, context)
63
+ # next_node = node.true_branch if condition_result else node.false_branch
64
+ # return self._traverse_tree(next_node, context)
65
+ # else:
66
+ # raise ValueError("Invalid node type.")
@@ -0,0 +1,59 @@
1
+ from typing import Any
2
+ from pydantic import field_serializer
3
+ from functools import singledispatchmethod
4
+ from lionagi import logging as _logging
5
+ from lionagi.libs import func_call, AsyncUtil
6
+ from lionagi.core.generic.node import Node
7
+ from .function_calling import FunctionCalling
8
+
9
+
10
+ class Tool(Node):
11
+
12
+ func: Any
13
+ schema_: dict | None = None
14
+ manual: Any | None = None
15
+ parser: Any | None = None
16
+ pre_processor: Any | None = None
17
+ post_processor: Any | None = None
18
+
19
+ @property
20
+ def name(self):
21
+ return self.schema_["function"]["name"]
22
+
23
+ @field_serializer("func")
24
+ def serialize_func(self, func):
25
+ return func.__name__
26
+
27
+ @singledispatchmethod
28
+ async def invoke(self, values: Any) -> Any:
29
+ raise TypeError(f"Unsupported type {type(values)}")
30
+
31
+ @invoke.register
32
+ async def _(self, kwargs: dict):
33
+
34
+ out = None
35
+
36
+ if self.pre_processor:
37
+ kwargs = await func_call.unified_call(self.pre_processor, kwargs)
38
+ try:
39
+ out = await func_call.unified_call(self.func, **kwargs)
40
+
41
+ except Exception as e:
42
+ _logging.error(f"Error invoking function {self.func_name}: {e}")
43
+ return None
44
+
45
+ if self.post_processor:
46
+ return await func_call.unified_call(self.post_processor, out)
47
+
48
+ return out
49
+
50
+ @invoke.register
51
+ async def _(self, function_calls: FunctionCalling):
52
+ return await self.invoke(function_calls.kwargs)
53
+
54
+ @invoke.register
55
+ async def _(self, values: list):
56
+ return await func_call.alcall(self.invoke, values)
57
+
58
+
59
+ TOOL_TYPE = bool | Tool | str | list[Tool | str | dict] | dict
@@ -0,0 +1,138 @@
1
+ import asyncio
2
+ from functools import singledispatchmethod
3
+ from collections import deque
4
+ from typing import Tuple, Any, TypeVar, Callable
5
+ from lionagi.libs import func_call, convert, ParseUtil
6
+ from lionagi import logging as _logging
7
+ from .schema import Tool, TOOL_TYPE
8
+ from .util import func_to_tool, parse_tool_response
9
+ from .function_calling import FunctionCalling
10
+
11
+ T = TypeVar("T", bound=Tool)
12
+
13
+
14
+ class ToolManager:
15
+
16
+ def __init__(
17
+ self,
18
+ tool_registry: dict = {},
19
+ function_calling_tasks: dict[str : deque[FunctionCalling]] = {},
20
+ ):
21
+ self.registry = tool_registry
22
+ self.function_calling_tasks = function_calling_tasks
23
+
24
+ @singledispatchmethod
25
+ def register_tools(self, tools: Any):
26
+ raise TypeError(f"Unsupported type {type(tools)}")
27
+
28
+ @register_tools.register(Tool)
29
+ def _(self, tools):
30
+ name = tools.schema_["function"]["name"]
31
+ if self._has_name(name):
32
+ err_msg = f"Function {name} is already registered."
33
+ _logging.error(err_msg)
34
+ raise ValueError(err_msg)
35
+ else:
36
+ self.registry[name] = tools
37
+ self.function_calling_tasks[name] = deque()
38
+ return True
39
+
40
+ @register_tools.register(Callable)
41
+ def _(self, tools):
42
+ tool = func_to_tool(tools)[0]
43
+ return self.register_tools(tool)
44
+
45
+ @register_tools.register(list)
46
+ def _(self, tools):
47
+ return func_call.lcall(tools, self.register_tools)
48
+
49
+ @singledispatchmethod
50
+ def register_function_calling(self, func_params: Any):
51
+ raise TypeError(f"Unsupported type {type(func_params)}")
52
+
53
+ @register_function_calling.register(tuple)
54
+ def _(self, func_params):
55
+ func = self.registry[func_params[0]].func
56
+ kwargs = func_params[1]
57
+ _function_calling = FunctionCalling(func=func, kwargs=kwargs)
58
+ self.function_calling_tasks[func.__name__].append(_function_calling)
59
+ return True
60
+
61
+ @register_function_calling.register(dict)
62
+ def _(self, response):
63
+ tuple_ = parse_tool_response(response)
64
+ return self.register_function_calling(tuple_)
65
+
66
+ @register_function_calling.register(list)
67
+ def _(self, func_params):
68
+ return func_call.lcall(func_params, self.register_function_calling)
69
+
70
+ async def invoke(self, func_params: Tuple[str, dict[str, Any]]) -> Any:
71
+ name, kwargs = func_params
72
+ if not self._has_name(name):
73
+ raise ValueError(f"Function {name} is not registered.")
74
+ tool = self.registry[name]
75
+ func = tool.func
76
+ parser = tool.parser
77
+ try:
78
+ out = await func_call.unified_call(func, **kwargs)
79
+ return parser(out) if parser else out
80
+
81
+ except Exception as e:
82
+ raise ValueError(
83
+ f"Error when invoking function {name} with arguments {kwargs} with error message {e}"
84
+ ) from e
85
+
86
+ @property
87
+ def _schema_list(self) -> list[dict[str, Any]]:
88
+ return [tool.schema_ for tool in self.registry.values()]
89
+
90
+ def get_tool_schema(self, tools: TOOL_TYPE, **kwargs):
91
+ if isinstance(tools, bool):
92
+ tool_kwarg = {"tools": self._schema_list}
93
+ return tool_kwarg | kwargs
94
+
95
+ else:
96
+ if not isinstance(tools, list):
97
+ tools = [tools]
98
+ tool_kwarg = {"tools": self._get_tool_schema(tools)}
99
+ return tool_kwarg | kwargs
100
+
101
+ def _has_name(self, name: str) -> bool:
102
+ return name in self.registry
103
+
104
+ @singledispatchmethod
105
+ def _get_tool_schema(self, tool: Any) -> dict:
106
+ raise TypeError(f"Unsupported type {type(tool)}")
107
+
108
+ @_get_tool_schema.register(dict)
109
+ def _(self, tool):
110
+ """
111
+ assuming that the tool is a schema
112
+ """
113
+ return tool
114
+
115
+ @_get_tool_schema.register(Tool)
116
+ def _(self, tool):
117
+ if self._has_name(tool.name):
118
+ return self.registry[tool.name].schema_
119
+ else:
120
+ err_msg = f"Function {tool.name} is not registered."
121
+ _logging.error(err_msg)
122
+ raise ValueError(err_msg)
123
+
124
+ @_get_tool_schema.register(str)
125
+ def _(self, tool):
126
+ """
127
+ assuming that the tool is a name
128
+ """
129
+ if self._has_name(tool):
130
+ return self.registry[tool].schema_
131
+ else:
132
+ err_msg = f"Function {tool} is not registered."
133
+ _logging.error(err_msg)
134
+ raise ValueError(err_msg)
135
+
136
+ @_get_tool_schema.register(list)
137
+ def _(self, tools):
138
+ return func_call.lcall(tools, self._get_tool_schema)
@@ -0,0 +1,16 @@
1
+ from typing import Tuple
2
+ from lionagi.libs import convert
3
+
4
+
5
+ def parse_tool_response(response: dict) -> Tuple[str, dict]:
6
+ try:
7
+ func = response["action"][7:]
8
+ args = convert.to_dict(response["arguments"])
9
+ return func, args
10
+ except Exception:
11
+ try:
12
+ func = response["recipient_name"].split(".")[-1]
13
+ args = response["parameters"]
14
+ return func, args
15
+ except:
16
+ raise ValueError("response is not a valid function call")
File without changes
@@ -0,0 +1,25 @@
1
+ from collections import deque
2
+ from typing import Dict
3
+ from pydantic import BaseModel, Field
4
+ from .schema import Work, WorkStatus
5
+
6
+
7
+ class WorkLog(BaseModel):
8
+ """Model to store and manage work logs."""
9
+
10
+ logs: Dict[str, Work] = Field(default={}, description="Logs of work items")
11
+ pending: deque = Field(
12
+ default_factory=deque, description="Priority queue of pending work items"
13
+ )
14
+ errored: deque = Field(
15
+ default_factory=deque, description="Queue of errored work items"
16
+ )
17
+
18
+ def append(self, work: Work):
19
+ """Append a work item to the logs and pending queue."""
20
+ self.logs[str(work.form_id)] = work
21
+ self.pending.append(str(work.form_id))
22
+
23
+ def get_by_status(self, status: WorkStatus) -> Dict[str, Work]:
24
+ """Get work items by their status."""
25
+ return {wid: work for wid, work in self.logs.items() if work.status == status}
File without changes
@@ -0,0 +1,30 @@
1
+ from enum import Enum
2
+ from typing import Any, Dict, List
3
+ from pydantic import Field
4
+ from lionagi.core.generic import BaseComponent
5
+
6
+
7
+ class WorkStatus(str, Enum):
8
+ """Enum to represent different statuses of work."""
9
+
10
+ PENDING = "PENDING"
11
+ IN_PROGRESS = "IN_PROGRESS"
12
+ COMPLETED = "COMPLETED"
13
+ FAILED = "FAILED"
14
+ CANCELLED = "CANCELLED"
15
+
16
+
17
+ class Work(BaseComponent):
18
+ """Base component for handling individual units of work."""
19
+
20
+ form_id: str = Field(..., description="ID of the form for this work")
21
+ priority: int = Field(default=0, description="Priority of the work")
22
+ status: WorkStatus = Field(
23
+ default=WorkStatus.PENDING, description="Current status of the work"
24
+ )
25
+ deliverables: Dict[str, Any] | list = Field(
26
+ default={}, description="Deliverables produced by the work"
27
+ )
28
+ dependencies: List["Work"] = Field(
29
+ default_factory=list, description="List of work items this work depends on"
30
+ )
@@ -0,0 +1,72 @@
1
+ from .schema import Work, WorkStatus
2
+ from ._logger import WorkLog
3
+ from .work_function import WorkFunction
4
+
5
+ import unittest
6
+ from unittest.mock import AsyncMock, patch
7
+
8
+ from lionagi.libs import func_call
9
+
10
+
11
+ class TestWork(unittest.TestCase):
12
+ def setUp(self):
13
+ self.work = Work(form_id="123")
14
+
15
+ def test_initial_status(self):
16
+ """Test the initial status is set to PENDING."""
17
+ self.assertEqual(self.work.status, WorkStatus.PENDING)
18
+
19
+ def test_initial_deliverables(self):
20
+ """Test the initial deliverables are empty."""
21
+ self.assertEqual(self.work.deliverables, {})
22
+
23
+ def test_initial_dependencies(self):
24
+ """Test the initial dependencies are empty."""
25
+ self.assertEqual(self.work.dependencies, [])
26
+
27
+
28
+ class TestWorkLog(unittest.TestCase):
29
+ def setUp(self):
30
+ self.work_log = WorkLog()
31
+ self.work = Work(form_id="123")
32
+ self.work_log.append(self.work)
33
+
34
+ def test_append_work(self):
35
+ """Test appending work adds to logs and pending queue."""
36
+ self.assertIn("123", self.work_log.logs)
37
+ self.assertIn("123", self.work_log.pending)
38
+
39
+ def test_get_by_status(self):
40
+ """Test retrieving works by status."""
41
+ result = self.work_log.get_by_status(WorkStatus.PENDING)
42
+ self.assertEqual(result, {"123": self.work})
43
+
44
+
45
+ class TestWorkFunction(unittest.TestCase):
46
+ def setUp(self):
47
+ self.work_function = WorkFunction(function=AsyncMock(return_value="result"))
48
+ self.work = Work(form_id="123")
49
+ self.work_log = WorkLog()
50
+ self.work_log.append(self.work)
51
+ self.work_function.worklog = self.work_log
52
+
53
+ @patch("asyncio.sleep", new_callable=AsyncMock)
54
+ async def test_execute(self, mocked_sleep):
55
+ """Test executing work changes its status and handles results."""
56
+ with patch.object(func_call, "rcall", new_callable=AsyncMock) as mock_rcall:
57
+ mock_rcall.return_value = "completed"
58
+ await self.work_function.execute()
59
+ self.assertEqual(self.work.status, WorkStatus.COMPLETED)
60
+ self.assertNotIn("123", self.work_function.worklog.pending)
61
+
62
+ @patch("asyncio.sleep", new_callable=AsyncMock)
63
+ async def test_execute_failure(self, mocked_sleep):
64
+ """Test handling failure during work execution."""
65
+ with patch.object(func_call, "rcall", side_effect=Exception("Error")):
66
+ await self.work_function.execute()
67
+ self.assertEqual(self.work.status, WorkStatus.FAILED)
68
+ self.assertIn("123", self.work_function.worklog.errored)
69
+
70
+
71
+ if __name__ == "__main__":
72
+ unittest.main()
File without changes
@@ -0,0 +1,89 @@
1
+ import asyncio
2
+ from typing import Any, Callable, Dict, List
3
+ from pydantic import Field
4
+ from functools import wraps
5
+ from lionagi import logging as _logging
6
+ from lionagi.libs import func_call
7
+ from lionagi.core.generic import BaseComponent
8
+
9
+ from .schema import Work, WorkStatus
10
+ from ._logger import WorkLog
11
+ from .worker import Worker
12
+
13
+
14
+ class WorkFunction(BaseComponent):
15
+ """Work function management and execution."""
16
+
17
+ function: Callable
18
+ args: List[Any] = Field(default_factory=list)
19
+ kwargs: Dict[str, Any] = Field(default_factory=dict)
20
+ retry_kwargs: Dict[str, Any] = Field(default_factory=dict)
21
+ worklog: WorkLog = Field(default_factory=WorkLog)
22
+ instruction: str = Field(
23
+ default="", description="Instruction for the work function"
24
+ )
25
+ refresh_time: float = Field(
26
+ default=0.5, description="Time to wait before checking for pending work"
27
+ )
28
+
29
+ @property
30
+ def name(self):
31
+ """Get the name of the work function."""
32
+ return self.function.__name__
33
+
34
+ async def execute(self):
35
+ """Execute pending work items."""
36
+ while self.worklog.pending:
37
+ work_id = self.worklog.pending.popleft()
38
+ work = self.worklog.logs[work_id]
39
+ if work.status == WorkStatus.PENDING:
40
+ try:
41
+ await func_call.rcall(self._execute, work, **work.retry_kwargs)
42
+ except Exception as e:
43
+ work.status = WorkStatus.FAILED
44
+ _logging.error(f"Work {work.id_} failed with error: {e}")
45
+ self.worklog.errored.append(work.id_)
46
+ else:
47
+ _logging.warning(
48
+ f"Work {work.id_} is in {work.status} state "
49
+ "and cannot be executed."
50
+ )
51
+ await asyncio.sleep(self.refresh_time)
52
+
53
+ async def _execute(self, work: Work):
54
+ """Execute a single work item."""
55
+ work.status = WorkStatus.IN_PROGRESS
56
+ result = await self.function(*self.args, **self.kwargs)
57
+ work.deliverables = result
58
+ work.status = WorkStatus.COMPLETED
59
+ return result
60
+
61
+
62
+ def workfunc(func):
63
+
64
+ @wraps(func)
65
+ async def wrapper(self: Worker, *args, **kwargs):
66
+ # Retrieve the worker instance ('self')
67
+ if not hasattr(self, "work_functions"):
68
+ self.work_functions = {}
69
+
70
+ if func.__name__ not in self.work_functions:
71
+ # Create WorkFunction with the function and its docstring as instruction
72
+ self.work_functions[func.__name__] = WorkFunction(
73
+ function=func,
74
+ instruction=func.__doc__,
75
+ args=args,
76
+ kwargs=kwargs,
77
+ retry_kwargs=kwargs.pop("retry_kwargs", {}),
78
+ )
79
+
80
+ # Retrieve the existing WorkFunction
81
+ work_function: WorkFunction = self.work_functions[func.__name__]
82
+ # Update args and kwargs for this call
83
+ work_function.args = args
84
+ work_function.kwargs = kwargs
85
+
86
+ # Execute the function using WorkFunction's managed execution process
87
+ return await work_function.execute()
88
+
89
+ return wrapper
@@ -0,0 +1,12 @@
1
+ from abc import ABC
2
+ from pydantic import Field
3
+ from lionagi.core.generic import BaseComponent
4
+
5
+
6
+ class Worker(BaseComponent, ABC):
7
+ form_templates: dict = Field(
8
+ default={}, description="The form templates of the worker"
9
+ )
10
+ work_functions: dict = Field(
11
+ default={}, description="The work functions of the worker"
12
+ )
File without changes
@@ -0,0 +1,124 @@
1
+ from typing import Dict, Union
2
+
3
+
4
+ def get_ipython_user_proxy():
5
+
6
+ try:
7
+ from lionagi.libs import SysUtil
8
+
9
+ SysUtil.check_import("autogen", pip_name="pyautogen")
10
+
11
+ import autogen
12
+ from IPython import get_ipython
13
+ except Exception as e:
14
+ raise ImportError(f"Please install autogen and IPython. {e}")
15
+
16
+ class IPythonUserProxyAgent(autogen.UserProxyAgent):
17
+
18
+ def __init__(self, name: str, **kwargs):
19
+ super().__init__(name, **kwargs)
20
+ self._ipython = get_ipython()
21
+
22
+ def generate_init_message(self, *args, **kwargs) -> Union[str, Dict]:
23
+ return (
24
+ super().generate_init_message(*args, **kwargs)
25
+ + """If you suggest code, the code will be executed in IPython."""
26
+ )
27
+
28
+ def run_code(self, code, **kwargs):
29
+ result = self._ipython.run_cell("%%capture --no-display cap\n" + code)
30
+ log = self._ipython.ev("cap.stdout")
31
+ log += self._ipython.ev("cap.stderr")
32
+ if result.result is not None:
33
+ log += str(result.result)
34
+ exitcode = 0 if result.success else 1
35
+ if result.error_before_exec is not None:
36
+ log += f"\n{result.error_before_exec}"
37
+ exitcode = 1
38
+ if result.error_in_exec is not None:
39
+ log += f"\n{result.error_in_exec}"
40
+ exitcode = 1
41
+ return exitcode, log, None
42
+
43
+ return IPythonUserProxyAgent
44
+
45
+
46
+ def get_autogen_coder(
47
+ llm_config=None,
48
+ code_execution_config=None,
49
+ kernal="python",
50
+ config_list=None,
51
+ max_consecutive_auto_reply=15,
52
+ temperature=0,
53
+ cache_seed=42,
54
+ env_="local",
55
+ assistant_instruction=None,
56
+ ):
57
+ assistant = ""
58
+ try:
59
+ from lionagi.libs import SysUtil
60
+
61
+ SysUtil.check_import("autogen", pip_name="pyautogen")
62
+
63
+ import autogen
64
+ from autogen.agentchat.contrib.gpt_assistant_agent import GPTAssistantAgent
65
+ except Exception as e:
66
+ raise ImportError(f"Please install autogen. {e}")
67
+
68
+ if env_ == "local":
69
+ assistant = autogen.AssistantAgent(
70
+ name="assistant",
71
+ llm_config=llm_config
72
+ or {
73
+ "cache_seed": cache_seed,
74
+ "config_list": config_list,
75
+ "temperature": temperature,
76
+ },
77
+ )
78
+
79
+ elif env_ == "oai_assistant":
80
+ assistant = GPTAssistantAgent(
81
+ name="Coder Assistant",
82
+ llm_config={
83
+ "tools": [{"type": "code_interpreter"}],
84
+ "config_list": config_list,
85
+ },
86
+ instructions=assistant_instruction,
87
+ )
88
+
89
+ if kernal == "python":
90
+ user_proxy = autogen.UserProxyAgent(
91
+ name="user_proxy",
92
+ human_input_mode="NEVER",
93
+ max_consecutive_auto_reply=max_consecutive_auto_reply,
94
+ is_termination_msg=lambda x: x.get("content", "")
95
+ .rstrip()
96
+ .endswith("TERMINATE"),
97
+ code_execution_config=code_execution_config
98
+ or {
99
+ "work_dir": "coding",
100
+ "use_docker": False,
101
+ },
102
+ )
103
+ return user_proxy, assistant
104
+
105
+ elif kernal == "ipython":
106
+ user_proxy = get_ipython_user_proxy(
107
+ "ipython_user_proxy",
108
+ human_input_mode="NEVER",
109
+ max_consecutive_auto_reply=max_consecutive_auto_reply,
110
+ is_termination_msg=lambda x: x.get("content", "")
111
+ .rstrip()
112
+ .endswith("TERMINATE")
113
+ or x.get("content", "").rstrip().endswith('"TERMINATE".'),
114
+ )
115
+ return user_proxy, assistant
116
+
117
+ # # Sample Usage Pattern
118
+ # context = "def my_function():\n pass\n"
119
+ # task1 = "I need help with the following code:\n"
120
+ # task2 = "Please write a function that returns the sum of two numbers."
121
+
122
+ # user_proxy, assistant = get_autogen_coder()
123
+ # user_proxy.initiate_chat(assistant, message=task1+context)
124
+ # user_proxy.send(recipient=assistant, message=task2)