Skip to content

Commit 961e558

Browse files
author
zhang_lu
committed
Add concepts docs
1 parent 478e962 commit 961e558

File tree

14 files changed

+577
-37
lines changed

14 files changed

+577
-37
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ It is recommended to deploy Conductor using Docker:
5050
```bash
5151
docker compose -f docker/conductor/docker-compose.yml up -d
5252
```
53-
- Once deployed, you can access the Conductor UI at `http://localhost:5000`.
53+
- Once deployed, you can access the Conductor UI at `http://localhost:5001`. (Note: Mac system will occupy port 5000 by default, so we use 5001 here. You can specify other ports when deploying Conductor.)
5454
- The Conductor API can be accessed via `http://localhost:8080`.
5555

5656
### 2. Install OmAgent

README_ZH.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ OmAgent 使用 [Conductor](https://github.com/conductor-oss/conductor) 作为工
4848
```bash
4949
docker compose -f docker/conductor/docker-compose.yml up -d
5050
```
51-
- 部署完成后可以通过访问 `http://localhost:5000` 访问Conductor UI。
51+
- 部署完成后可以通过访问 `http://localhost:5001` 访问Conductor UI。(注:Mac系统默认会占用5000端口,因此我们使用5001端口,你可以在部署Conductor的时候指定其它端口。)
5252
- 通过 `http://localhost:8080` 调用Conductor API。
5353

5454
### 2. 安装OmAgent

docker/conductor/docker-compose.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ services:
1111
- internal
1212
ports:
1313
- 8080:8080
14-
- 5000:5000
14+
- 5001:5000
1515
healthcheck:
1616
test: ["CMD", "curl","-I" ,"-XGET", "http://localhost:8080/health"]
1717
interval: 60s

docs/concepts/container.md

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Container
2+
3+
The Container module is a dependency injection and service container implementation that manages components and their dependencies in the OmAgent core system. It follows the IoC (Inversion of Control) pattern to handle component registration, configuration, and retrieval.
4+
5+
## Key Features
6+
### 1. Component Management
7+
- Registers and manages different types of components (connections, memories, callbacks, etc.)
8+
- Handles component dependencies automatically
9+
- Provides type-safe access to registered components
10+
11+
### 2. Connector Management
12+
Manages service connectors that components might depend on
13+
- Automatically injects required connectors into components
14+
15+
### 3. Special Component Types
16+
- STM (Short-term Memory)
17+
- LTM (Long-term Memory)
18+
- Callback handlers
19+
- Input handlers
20+
21+
### 4. Configuration Management
22+
- Can compile configurations to YAML
23+
- Loads configurations from YAML files
24+
- Supports environment variables and descriptions in configs
25+
26+
27+
## Register
28+
Examples of registering:
29+
```python
30+
from omagent_core.utils.container import container
31+
from omagent_core.services.handlers.redis_stream_handler import RedisStreamHandler
32+
33+
# Register a connector using component name
34+
container.register_connector(RedisConnector, name="redis_client")
35+
36+
# Register a component using component class
37+
container.register_component(RedisStreamHandler)
38+
39+
# Register STM component
40+
container.register_stm("RedisSTM")
41+
42+
# Register LTM component
43+
container.register_ltm("MilvusLTM")
44+
45+
# Register callback and input handlers
46+
container.register_callback("AppCallback")
47+
container.register_input("AppInput")
48+
```
49+
50+
## Configuration Management
51+
52+
1. **Compile Configuration**: Container can automatically generate YAML configuration template files. You can change the values of the parameters in the template files which will take effect when loading the configuration. The ```env_var``` indicates the environment variable names for the parameters, don't change it because it is just for demonstration.
53+
```python
54+
from pathlib import Path
55+
container.compile_config(Path('./config_dir'))
56+
```
57+
58+
59+
60+
2. **Load Configuration**: Load settings from YAML files. This will update the container with the settings in the YAML file.
61+
```python
62+
container.from_config('container.yaml')
63+
```
64+
65+
## Component Retrieval
66+
67+
Access registered components:
68+
```python
69+
# Get a connector
70+
redis_client = container.get_connector("redis_client")
71+
72+
# Get STM component
73+
stm = container.stm
74+
75+
# Get LTM component
76+
ltm = container.ltm
77+
78+
# Get callback handler
79+
callback = container.callback
80+
81+
# Get input handler
82+
input_handler = container.input
83+
```
84+
85+
86+
## Best Practices
87+
88+
1. **Early Registration**: Register all components at application startup
89+
2. **Configuration Files**: Use YAML configuration files for better maintainability
90+
3. **Compile Configuration**: Prepare a separated script to compile container configuration before application startup.
91+
4. **Update Container**: Update the container with the settings in project entry file. Do register default Special Components (STM, LTM, Callback, Input) before update.
92+
5. **Single Instance**: Use the global container instance provided by the framework

docs/concepts/register.md

Whitespace-only changes.

docs/concepts/registry.md

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Register
2+
3+
The Registry module is a powerful tool for managing and organizing different types of modules in your application. It supports registration and retrieval of various categories like prompts, LLMs, workers, tools, encoders, connectors, and components.
4+
5+
## Registration
6+
You can register classes using either decorators or direct registration:
7+
```python
8+
from omagent_core.utils.registry import registry
9+
10+
# Using decorator (recommended)
11+
@registry.register_node()
12+
class MyNode:
13+
name = "MyNode"
14+
15+
# Or with a custom name
16+
@registry.register_tool(name="custom_tool_name")
17+
class MyTool:
18+
pass
19+
20+
# Direct registration
21+
class MyLLM:
22+
pass
23+
registry.register("llm", "my_llm")(MyLLM)
24+
```
25+
26+
27+
## Retrieval
28+
Retrieve registered modules using the get methods:
29+
```python
30+
# Get registered modules
31+
my_node = registry.get_node("MyNode")
32+
my_tool = registry.get_tool("custom_tool_name")
33+
my_llm = registry.get("llm", "my_llm")
34+
```
35+
36+
37+
## Auto-Import Feature
38+
The registry can automatically import modules from specified paths:
39+
```python
40+
# Import from default paths
41+
registry.import_module()
42+
43+
# Import from custom project path
44+
registry.import_module("path/to/your/modules")
45+
46+
# Import from multiple paths
47+
registry.import_module([
48+
"path/to/modules1",
49+
"path/to/modules2"
50+
])
51+
```
52+
Note: Do use the ```registry.import_module()``` in the main function of your script so that the modules can be registered to python environment before being used.
53+

docs/concepts/task.md

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# Task
2+
3+
Task is the basic unit of building workflow. There are two types of tasks: simple task and operator.
4+
5+
## Simple Task
6+
The functionality of simple task is defined by binding it to a [worker](./worker.md).
7+
Here is an example of how to define a simple task:
8+
```python
9+
from omagent_core.engine.worker.base import BaseWorker
10+
from omagent_core.engine.workflow.conductor_workflow import ConductorWorkflow
11+
from omagent_core.engine.workflow.task.simple_task import simple_task
12+
from omagent_core.utils.registry import registry
13+
14+
# Define a worker
15+
@registry.register_worker()
16+
class SimpleWorker(BaseWorker):
17+
def _run(self, my_name: str):
18+
return {}
19+
20+
# Define a workflow
21+
workflow = ConductorWorkflow(name='my_exp')
22+
23+
# Define a simple task
24+
task = simple_task(task_def_name='SimpleWorker', task_reference_name='ref_name', inputs={'my_name': workflow.input('my_name')})
25+
26+
workflow >> task
27+
```
28+
Specify the task definition name(```task_def_name```) and the task reference name(```task_reference_name```). The task definition name should be the name of the corresponding worker class. The task reference name is used to identify the task in the workflow.
29+
Specify the inputs of the task. Inputs may be either values or references to a workflow's initial inputs or the outputs of preceding tasks.
30+
See [workflow](./workflow.md) for workflow details.
31+
32+
## Operators
33+
Operators are the build-in tasks provided by the workflow engine. They handle the workflow control logic.
34+
### 1. Switch Task
35+
Switch task is used to make a decision based on the value of a given field.
36+
```python
37+
from omagent_core.engine.workflow.task.switch_task import SwitchTask
38+
from omagent_core.engine.worker.base import BaseWorker
39+
from omagent_core.engine.workflow.conductor_workflow import ConductorWorkflow
40+
from omagent_core.engine.workflow.task.simple_task import simple_task
41+
from omagent_core.utils.registry import registry
42+
43+
@registry.register_worker()
44+
class SimpleWorker1(BaseWorker):
45+
def _run(self):
46+
print('worker1')
47+
return {}
48+
49+
@registry.register_worker()
50+
class SimpleWorker2(BaseWorker):
51+
def _run(self):
52+
print('worker2')
53+
return {}
54+
55+
@registry.register_worker()
56+
class SimpleWorker3(BaseWorker):
57+
def _run(self):
58+
print('worker3')
59+
return {}
60+
61+
workflow = ConductorWorkflow(name='switch_test')
62+
63+
# Create some example tasks (replace with your actual tasks)
64+
task1 = simple_task(task_def_name='SimpleWorker1', task_reference_name='ref_name1')
65+
task2 = simple_task(task_def_name='SimpleWorker2', task_reference_name='ref_name2')
66+
task3 = simple_task(task_def_name='SimpleWorker3', task_reference_name='ref_name3')
67+
68+
# 1. Create a switch task with a value-based condition
69+
switch = SwitchTask(
70+
task_ref_name="my_switch",
71+
case_expression=workflow.input('switch_case_value'), # This will evaluate the switch_case_value from workflow input
72+
)
73+
74+
# 2. Add cases
75+
switch.switch_case("w1", [task1])
76+
switch.switch_case("w2", [task2])
77+
78+
# 3. Add default case (optional)
79+
switch.default_case([task3])
80+
81+
workflow >> switch
82+
83+
workflow.register(overwrite=True)
84+
```
85+
This will create a basic workflow with a switch task shown below.
86+
<p align="center">
87+
<img src="../images/switch_task.png" width="300"/>
88+
</p>
89+
You can also chaining the switch cases as follows:
90+
91+
```python
92+
switch.switch_case("w1", [task1]).switch_case("w2", [task2]).default_case([task3])
93+
```
94+
95+
### 2. Fork-Join Task
96+
The fork-join task is used to execute multiple tasks in parallel.
97+
```python
98+
from omagent_core.engine.workflow.task.fork_task import ForkTask
99+
from omagent_core.engine.worker.base import BaseWorker
100+
from omagent_core.engine.workflow.conductor_workflow import ConductorWorkflow
101+
from omagent_core.engine.workflow.task.simple_task import simple_task
102+
from omagent_core.utils.registry import registry
103+
104+
105+
@registry.register_worker()
106+
class SimpleWorker1(BaseWorker):
107+
def _run(self):
108+
print("worker1")
109+
return {}
110+
111+
112+
@registry.register_worker()
113+
class SimpleWorker2(BaseWorker):
114+
def _run(self):
115+
print("worker2")
116+
return {}
117+
118+
119+
@registry.register_worker()
120+
class SimpleWorker3(BaseWorker):
121+
def _run(self):
122+
print("worker3")
123+
return {}
124+
125+
126+
# Create the main workflow
127+
workflow = ConductorWorkflow(name="fork_join_test")
128+
129+
# Create tasks for parallel execution
130+
task1 = simple_task(task_def_name="SimpleWorker1", task_reference_name="parallel_task1")
131+
task2 = simple_task(task_def_name="SimpleWorker2", task_reference_name="parallel_task2")
132+
task3 = simple_task(task_def_name="SimpleWorker3", task_reference_name="parallel_task3")
133+
134+
# Create parallel execution paths
135+
path1 = [task1] # First parallel path
136+
path2 = [task2] # Second parallel path
137+
path3 = [task3] # Third parallel path
138+
139+
# Create the fork task with multiple parallel paths
140+
fork_task = ForkTask(
141+
task_ref_name="parallel_execution",
142+
forked_tasks=[path1, path2, path3],
143+
# The join will wait for the last task in each path
144+
join_on=["parallel_task1", "parallel_task2", "parallel_task3"]
145+
)
146+
147+
# Add the fork task to the workflow
148+
workflow.add(fork_task)
149+
150+
workflow.register(overwrite=True)
151+
```
152+
This will create a basic workflow with a fork-join task shown below.
153+
<p align="center">
154+
<img src="../images/fork_task.png" width="300"/>
155+
</p>
156+
157+
### 3. Do-While Task

0 commit comments

Comments
 (0)