Compare commits
1 Commits
pipeline-l
...
pipeline
| Author | SHA1 | Date | |
|---|---|---|---|
| 8945e922d5 |
Binary file not shown.
|
Before Width: | Height: | Size: 169 KiB After Width: | Height: | Size: 98 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -18,11 +18,7 @@
|
|||||||
{"name": "takeoff", "description": "无人机从当前位置垂直起飞到指定的海拔高度。", "params": {"altitude": "float, 目标海拔高度(米),范围[1, 100],默认为2"}},
|
{"name": "takeoff", "description": "无人机从当前位置垂直起飞到指定的海拔高度。", "params": {"altitude": "float, 目标海拔高度(米),范围[1, 100],默认为2"}},
|
||||||
{"name": "land", "description": "降落无人机。可选择当前位置或返航点降落。", "params": {"mode": "string, 可选值: 'current'(当前位置), 'home'(返航点)"}},
|
{"name": "land", "description": "降落无人机。可选择当前位置或返航点降落。", "params": {"mode": "string, 可选值: 'current'(当前位置), 'home'(返航点)"}},
|
||||||
{"name": "fly_to_waypoint", "description": "导航至一个指定坐标点。使用相对坐标系(x,y,z),单位为米。", "params": {"x": "float", "y": "float", "z": "float", "acceptance_radius": "float, 可选,默认2.0"}},
|
{"name": "fly_to_waypoint", "description": "导航至一个指定坐标点。使用相对坐标系(x,y,z),单位为米。", "params": {"x": "float", "y": "float", "z": "float", "acceptance_radius": "float, 可选,默认2.0"}},
|
||||||
{"name": "move_direction", "description": "按指定方向直线移动。方向可为绝对方位或相对机体朝向。", "params": {"direction": "string: north|south|east|west|forward|backward|left|right", "distance": "float[1,10000], 可选, 不指定则持续移动", "speed": "float, 可选"}},
|
{"name": "move_direction", "description": "按指定方向直线移动。方向可为绝对方位或相对机体朝向。", "params": {"direction": "string: north|south|east|west|forward|backward|left|right", "distance": "float[1,10000], 可选, 不指定则持续移动"}},
|
||||||
{"name": "approach_target", "description": "快速趋近目标至固定距离。", "params": {"target_class": "string, 要趋近的目标类别", "description": "string, 可选", "stop_distance": "float, 期望的最终停止距离", "speed": "float, 可选"}},
|
|
||||||
{"name": "rotate", "description": "旋转固定角度。", "params": {"angle": "float, 旋转角度(正数逆时针, 负数顺时针)", "angular_velocity": "rad/s, 旋转角速度"}},
|
|
||||||
{"name": "rotate_search", "description": "原地旋转搜索目标。", "params": {"target_class": "string, 要搜寻的目标类别", "description": "string, 可选", "step_angle": "float, 可选, 每一步旋转的角度", "total_rotation": "float, 可选, 总共旋转搜索的角度"}},
|
|
||||||
{"name": "manual_confirmation", "description": "前端弹窗是否继续执行后续任务。", "params": {}},
|
|
||||||
{"name": "orbit_around_point", "description": "以给定中心点为中心,等速圆周飞行指定圈数。", "params": {"center_x": "float", "center_y": "float", "center_z": "float", "radius": "float[5,1000]", "laps": "int[1,20]", "clockwise": "boolean, 可选, 默认true", "speed_mps": "float[0.5,15], 可选", "gimbal_lock": "boolean, 可选, 默认true"}},
|
{"name": "orbit_around_point", "description": "以给定中心点为中心,等速圆周飞行指定圈数。", "params": {"center_x": "float", "center_y": "float", "center_z": "float", "radius": "float[5,1000]", "laps": "int[1,20]", "clockwise": "boolean, 可选, 默认true", "speed_mps": "float[0.5,15], 可选", "gimbal_lock": "boolean, 可选, 默认true"}},
|
||||||
{"name": "orbit_around_target", "description": "以目标为中心,等速圆周飞行指定圈数(需已有目标)。", "params": {"target_class": "string, 取值同object_detect列表", "description": "string, 可选", "radius": "float[5,1000]", "laps": "int[1,20]", "clockwise": "boolean, 可选, 默认true", "speed_mps": "float[0.5,15], 可选", "gimbal_lock": "boolean, 可选, 默认true"}},
|
{"name": "orbit_around_target", "description": "以目标为中心,等速圆周飞行指定圈数(需已有目标)。", "params": {"target_class": "string, 取值同object_detect列表", "description": "string, 可选", "radius": "float[5,1000]", "laps": "int[1,20]", "clockwise": "boolean, 可选, 默认true", "speed_mps": "float[0.5,15], 可选", "gimbal_lock": "boolean, 可选, 默认true"}},
|
||||||
{"name": "loiter", "description": "在当前位置上空悬停一段时间或直到条件触发。", "params": {"duration": "float, 可选[1,600]", "until_condition": "string, 可选"}},
|
{"name": "loiter", "description": "在当前位置上空悬停一段时间或直到条件触发。", "params": {"duration": "float, 可选[1,600]", "until_condition": "string, 可选"}},
|
||||||
|
|||||||
@@ -9,36 +9,29 @@
|
|||||||
{"name":"takeoff","params":{"altitude":"float[1,100],默认2"}},
|
{"name":"takeoff","params":{"altitude":"float[1,100],默认2"}},
|
||||||
{"name":"land","params":{"mode":"'current'/'home'"}},
|
{"name":"land","params":{"mode":"'current'/'home'"}},
|
||||||
{"name":"fly_to_waypoint","params":{"x":"±10000","y":"±10000","z":"[1,5000]","acceptance_radius":"默认2.0"}},
|
{"name":"fly_to_waypoint","params":{"x":"±10000","y":"±10000","z":"[1,5000]","acceptance_radius":"默认2.0"}},
|
||||||
{"name":"move_direction","params":{"direction":"north/south/east/west/forward/backward/left/right","distance":"[1,10000],缺省持续移动","speed":"float,可选"}},
|
{"name":"move_direction","params":{"direction":"north/south/east/west/forward/backward/left/right","distance":"[1,10000],缺省持续移动"}},
|
||||||
{"name":"approach_target","params":{"target_class":"string,要趋近的目标类别","description":"string,可选,目标属性描述","stop_distance":"float,期望的最终停止距离","speed":"float,可选,期望的逼近速度"}},
|
{"name":"orbit_around_point","params":{"center_x":"±10000","center_y":"±10000","center_z":"[1,5000]","radius":"[5,1000]","laps":"[1,20]","clockwise":"默认true","speed_mps":"[0.5,15]","gimbal_lock":"默认true"}},
|
||||||
{"name":"rotate","params":{"angle":"float,无人机自身旋转角度(正数逆时针,负数顺时针)","angular_velocity":"rad/s,旋转角速度"}},
|
{"name":"orbit_around_target","params":{"target_class":"见object_detect列表","description":"可选,目标属性","radius":"[5,1000]","laps":"[1,20]","clockwise":"默认true","speed_mps":"[0.5,15]","gimbal_lock":"默认true"}},
|
||||||
{"name":"rotate_search","params":{"target_class":"string,要搜寻的目标类别","description":"string,可选,目标属性描述","step_angle":"float,可选,每一步旋转的角度","total_rotation":"float,可选,总共旋转搜索的角度"}},
|
|
||||||
{"name":"manual_confirmation","params":{}},
|
|
||||||
{"name":"loiter","params":{"duration":"[1,600]秒/until_condition:可选"}},
|
{"name":"loiter","params":{"duration":"[1,600]秒/until_condition:可选"}},
|
||||||
{"name":"object_detect","params":{"target_class":"person,bicycle,car,motorcycle,airplane,bus,train,truck,boat,traffic_light,fire_hydrant,stop_sign,parking_meter,bench,bird,cat,dog,horse,sheep,cow,elephant,bear,zebra,giraffe,backpack,umbrella,handbag,tie,suitcase,frisbee,skis,snowboard,sports_ball,kite,baseball_bat,baseball_glove,skateboard,surfboard,tennis_racket,bottle,wine_glass,cup,fork,knife,spoon,bowl,banana,apple,sandwich,orange,broccoli,carrot,hot_dog,pizza,donut,cake,chair,couch,potted_plant,bed,dining_table,toilet,tv,laptop,mouse,remote,keyboard,cell_phone,microwave,oven,toaster,sink,refrigerator,book,clock,vase,scissors,teddy_bear,hair_drier,toothbrush,garbage","description":"可选,","count":"默认1"}},
|
{"name":"object_detect","params":{"target_class":"person,bicycle,car,motorcycle,airplane,bus,train,truck,boat,traffic_light,fire_hydrant,stop_sign,parking_meter,bench,bird,cat,dog,horse,sheep,cow,elephant,bear,zebra,giraffe,backpack,umbrella,handbag,tie,suitcase,frisbee,skis,snowboard,sports_ball,kite,baseball_bat,baseball_glove,skateboard,surfboard,tennis_racket,bottle,wine_glass,cup,fork,knife,spoon,bowl,banana,apple,sandwich,orange,broccoli,carrot,hot_dog,pizza,donut,cake,chair,couch,potted_plant,bed,dining_table,toilet,tv,laptop,mouse,remote,keyboard,cell_phone,microwave,oven,toaster,sink,refrigerator,book,clock,vase,scissors,teddy_bear,hair_drier,toothbrush","description":"可选,","count":"默认1"}},
|
||||||
{"name":"strike_target","params":{"target_class":"同object_detect","description":"可选,目标属性","count":"默认1"}},
|
{"name":"strike_target","params":{"target_class":"同object_detect","description":"可选,目标属性","count":"默认1"}},
|
||||||
{"name":"battle_damage_assessment","params":{"target_class":"同object_detect","assessment_time":"[5,60],默认15"}},
|
{"name":"battle_damage_assessment","params":{"target_class":"同object_detect","assessment_time":"[5,60],默认15"}},
|
||||||
{"name":"search_pattern","params":{"pattern_type":"spiral/grid","center_x":"±10000","center_y":"±10000","center_z":"[1,5000]","radius":"[5,1000]","target_class":"同object_detect","description":"可选,目标属性","count":"默认1"}},
|
{"name":"search_pattern","params":{"pattern_type":"spiral/grid","center_x":"±10000","center_y":"±10000","center_z":"[1,5000]","radius":"[5,1000]","target_class":"同object_detect","description":"可选,目标属性","count":"默认1"}},
|
||||||
{"name":"track_object","params":{"target_class":"同object_detect","description":"可选,目标属性","track_time":"[1,600]秒(必传,不可用'duration')","min_confidence":"[0.5,1.0]默认0.7","safe_distance":"[2,50]默认10"}},
|
{"name":"track_object","params":{"target_class":"同object_detect","description":"可选,目标属性","track_time":"[1,600]秒(必传,不可用'duration')","min_confidence":"[0.5,1.0]默认0.7","safe_distance":"[2,50]默认10"}},
|
||||||
{"name":"deliver_payload","params":{"payload_type":"string","release_altitude":"[2,100]默认5"}},
|
{"name":"deliver_payload","params":{"payload_type":"string","release_altitude":"[2,100]默认5"}},
|
||||||
{"name":"preflight_checks","params":{"check_level":"basic/comprehensive"}},
|
{"name":"preflight_checks","params":{"check_level":"basic/comprehensive"}},
|
||||||
{"name":"emergency_return","params":{"reason":"string"}},
|
{"name":"take_picture","params":{}}
|
||||||
{"name":"take_photos","params":{"target_class":"同object_detect","description":"可选,目标属性","track_time":"[1,600]秒(必传,不可用'duration')","min_confidence":"[0.5,1.0]默认0.7","safe_distance":"[2,50]默认10"}}
|
|
||||||
],
|
],
|
||||||
"conditions": [
|
"conditions": [
|
||||||
{"name":"at_waypoint","params":{"x":"±10000","y":"±10000","z":"[1,5000]","tolerance":"默认3.0"}},
|
{"name":"at_waypoint","params":{"x":"±10000","y":"±10000","z":"[1,5000]","tolerance":"默认3.0"}},
|
||||||
{"name":"object_detected","params":{"target_class":"同object_detect(必传)","description":"可选,目标属性","count":"默认1"}},
|
{"name":"object_detected","params":{"target_class":"同object_detect(必传)","description":"可选,目标属性","count":"默认1"}},
|
||||||
{"name":"target_destroyed","params":{"target_class":"同object_detect","description":"可选,目标属性","confidence":"[0.5,1.0]默认0.8"}},
|
{"name":"target_destroyed","params":{"target_class":"同object_detect","description":"可选,目标属性","confidence":"[0.5,1.0]默认0.8"}},
|
||||||
{"name":"time_elapsed","params":{"duration":"[1,2700]秒"}},
|
{"name":"time_elapsed","params":{"duration":"[1,2700]秒"}}
|
||||||
{"name":"gps_status","params":{"min_satellites":"int[6,15],必传(如8)"}}
|
|
||||||
],
|
],
|
||||||
"control_flow": [
|
"control_flow": [
|
||||||
{"name":"Sequence","params":{},"children":"子节点数组(按序执行,全成功则成功)"},
|
{"name":"Sequence","params":{},"children":"子节点数组(按序执行,全成功则成功)"},
|
||||||
{"name":"Selector","params":{"memory":"默认true"},"children":"子节点数组(执行到成功为止)"},
|
{"name":"Selector","params":{"memory":"默认true"},"children":"子节点数组(执行到成功为止)"},
|
||||||
{"name":"Parallel","params":{"policy":"all_success"},"children":"子节点数组(同时执行,严禁用'one_success')"}
|
{"name":"Parallel","params":{"policy":"all_success"},"children":"子节点数组(同时执行,严禁用'one_success')"}
|
||||||
],
|
|
||||||
"decorators": [
|
|
||||||
{"name":"SuccessIsFailure","params":{},"child":"单一子节点(将子节点的成功结果反转为失败)"}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -47,61 +40,35 @@
|
|||||||
## 二、节点必填字段(后端Schema强制要求,缺一验证失败)
|
## 二、节点必填字段(后端Schema强制要求,缺一验证失败)
|
||||||
每个节点必须包含以下字段,字段名/类型不可自定义:
|
每个节点必须包含以下字段,字段名/类型不可自定义:
|
||||||
1. **`type`**:
|
1. **`type`**:
|
||||||
- 动作节点→`"action"`,条件节点→`"condition"`,控制流节点→`"Sequence"`/`"Selector"`/`"Parallel"`,装饰器节点→`"decorator"`;
|
- 动作节点→`"action"`,条件节点→`"condition"`,控制流节点→`"Sequence"`/`"Selector"`/`"Parallel"`(与`name`字段值完全一致);
|
||||||
2. **`name`**:必须是上述JSON中定义的`name`值;
|
2. **`name`**:必须是上述JSON中`actions`/`conditions`/`control_flow`下的`name`值;
|
||||||
3. **`params`**:严格匹配上述节点的`params`定义,无自定义参数;
|
3. **`params`**:严格匹配上述节点的`params`定义,无自定义参数(如优先级排序不可加“priority”字段,仅用`description`);
|
||||||
4. **`children`**:仅控制流节点必含(子节点数组);
|
4. **`children`**:仅控制流节点必含(子节点数组),动作/条件节点无此字段。
|
||||||
5. **`child`**:仅装饰器节点必含(单一子节点对象,非数组)。
|
|
||||||
|
|
||||||
|
|
||||||
## 三、标准任务结构模板(单次起降流程)
|
## 三、行为树固定结构(通用不变)
|
||||||
大多数任务应遵循“起飞 -> 接近 -> 执行 -> 返航/降落”的单次闭环流程,参考结构如下:
|
根节点必须是`Parallel`,`children`含`MainTask`(Sequence),结构不随任务类型(含优先级排序)修改:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"root": {
|
"root": {
|
||||||
"type": "Sequence",
|
"type": "Parallel",
|
||||||
"name": "MainTask",
|
"name": "Mission",
|
||||||
|
"params": {"policy": "all_success"},
|
||||||
"children": [
|
"children": [
|
||||||
{"type":"action","name":"preflight_checks","params":{"check_level":"comprehensive"}},
|
|
||||||
{"type":"action","name":"takeoff","params":{"altitude":10.0}},
|
|
||||||
{"type":"action","name":"fly_to_waypoint","params":{"x":100.0,"y":50.0,"z":10.0}}, // 接近目标区域
|
|
||||||
// --- 核心任务区 (根据指令替换) ---
|
|
||||||
{"type":"action","name":"rotate_search","params":{"target_class":"person","description":"目标描述"}},
|
|
||||||
{"type":"action","name":"object_detect","params":{"target_class":"person","description":"目标描述"}},
|
|
||||||
// -------------------------------
|
|
||||||
// 默认不需要降落节点,除非用户明确要求
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 四、场景示例(请灵活参考)
|
|
||||||
|
|
||||||
#### 场景 1:线性搜索任务(Sequence + Selector)
|
|
||||||
**指令**:“去研究所正大门,搜索扎辫子女子并拍照。”
|
|
||||||
**结构**:Sequence (按顺序执行)
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"root": {
|
|
||||||
"type": "Sequence",
|
|
||||||
"name": "MainSearchTask",
|
|
||||||
"children": [
|
|
||||||
{"type":"action","name":"takeoff","params":{"altitude":10.0}},
|
|
||||||
{"type":"action","name":"fly_to_waypoint","params":{"x":100.0,"y":50.0,"z":10.0}},
|
|
||||||
{"type":"action","name":"rotate_search","params":{"target_class":"person","description":"扎辫子女子"}},
|
|
||||||
{
|
{
|
||||||
"type": "Selector",
|
"type": "Sequence",
|
||||||
"name": "CheckAndPhoto",
|
"name": "MainTask",
|
||||||
|
"params": {},
|
||||||
"children": [
|
"children": [
|
||||||
{
|
// 通用主任务步骤(含优先级排序任务示例,需按用户指令替换):
|
||||||
"type": "Sequence",
|
{"type":"action","name":"preflight_checks","params":{"check_level":"comprehensive"}},
|
||||||
"name": "PhotoIfFound",
|
{"type":"action","name":"takeoff","params":{"altitude":10.0}},
|
||||||
"children": [
|
{"type":"action","name":"fly_to_waypoint","params":{"x":200.0,"y":150.0,"z":10.0}}, // 搜索区坐标(用户未给时填合理值)
|
||||||
{"type":"condition","name":"object_detected","params":{"target_class":"person","description":"扎辫子女子"}},
|
{"type":"action","name":"search_pattern","params":{"pattern_type":"grid","center_x":200.0,"center_y":150.0,"center_z":10.0,"radius":50.0,"target_class":"balloon","description":"红色"}},
|
||||||
{"type":"action","name":"take_photos","params":{"target_class":"person","description":"扎辫子女子","track_time":10.0}}
|
{"type":"condition","name":"object_detected","params":{"target_class":"balloon","description":"红色"}}, // 确认高优先级目标
|
||||||
]
|
{"type":"action","name":"track_object","params":{"target_class":"balloon","description":"红色","track_time":30.0}},
|
||||||
},
|
{"type":"action","name":"strike_target","params":{"target_class":"balloon","description":"红色"}},
|
||||||
{"type":"action","name":"loiter","params":{"duration":5.0}} // 未发现时的备选动作
|
{"type":"action","name":"land","params":{"mode":"home"}}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -109,178 +76,21 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 场景 2:带中断逻辑的巡逻(Selector 示例)
|
|
||||||
**指令**:“飞往航点A。如果途中发现可疑人员,则悬停。”
|
|
||||||
**结构**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"root": {
|
|
||||||
"type": "Sequence",
|
|
||||||
"children": [
|
|
||||||
{"type":"action","name":"takeoff","params":{"altitude":10.0}},
|
|
||||||
{
|
|
||||||
"type": "Selector",
|
|
||||||
"name": "FlyOrDetect",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "Sequence",
|
|
||||||
"name": "InterruptionLogic",
|
|
||||||
"children": [
|
|
||||||
{"type":"action","name":"object_detect","params":{"target_class":"person"}},
|
|
||||||
{"type":"action","name":"loiter","params":{"duration":5.0}}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{"type":"action","name":"fly_to_waypoint","params":{"x":100.0,"y":50.0,"z":10.0}}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 场景 3:环绕侦察类任务
|
## 四、优先级排序任务通用示例
|
||||||
**指令**:“去面前的大楼三层/12米高,绕着外围看有没有打开的窗户,发现则进行拍照。”
|
当用户指令中明确提出有多个待考察且具有优先级关系的物体时,节点描述须为优先级关系。比如当指令为已知有三个气球,危险级关系为红色气球大于蓝色气球大于绿色气球,要求优先跟踪最危险的气球时,节点的描述参考下表情形。
|
||||||
**参考知识**:{"text": "面前的大楼外围四个点坐标:A(-24.00, 241.80),B(-108.50, 241.80),C(-108.50, 289.80),D(-24.00, 292.80)。"}
|
| 用户指令场景 | `target_class` | `description` | 核心节点示例(search_pattern) |
|
||||||
**结构**:Sequence (按顺序执行)
|
|-----------------------------|-----------------|-------------------------|------------------------------------------------------------------------------------------------|
|
||||||
```json
|
| 红气球>蓝气球>绿气球 | `balloon` | `(红>蓝>绿)` | `{"type":"action","name":"search_pattern","params":{"pattern_type":"grid","center_x":200,"center_y":150,"center_z":10,"radius":50,"target_class":"balloon","description":"(红>蓝>绿)"}}` |
|
||||||
{
|
| 军用卡车>民用卡车>面包车 | `truck` | `(军用卡车>民用卡车>面包车)` | `{"type":"action","name":"object_detect","params":{"target_class":"truck","description":"(军用卡车>民用卡车>面包车)"}}` |
|
||||||
"root": {
|
|
||||||
"type": "Sequence",
|
|
||||||
"name": "SurroundAndInspect",
|
|
||||||
"children": [
|
|
||||||
{"type":"action","name":"takeoff","params":{"altitude":12.0}},
|
|
||||||
// 移动到A点,并在此过程中持续检测
|
|
||||||
{
|
|
||||||
"type": "Parallel",
|
|
||||||
"name": "FlyAndInspectToA",
|
|
||||||
"children": [
|
|
||||||
{"type":"action","name":"fly_to_waypoint","params":{"x":-24.0,"y":241.8,"z":12.0}},
|
|
||||||
{
|
|
||||||
"type": "Selector",
|
|
||||||
"name": "OpportunisticPhoto",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "decorator",
|
|
||||||
"name": "SuccessIsFailure",
|
|
||||||
"child": {
|
|
||||||
"type": "Sequence",
|
|
||||||
"name": "DetectAndCapture",
|
|
||||||
"children": [
|
|
||||||
{"type":"action","name":"object_detect","params":{"target_class":"window","description":"open window"}},
|
|
||||||
{"type":"condition","name":"object_detected","params":{"target_class":"window","description":"open window"}},
|
|
||||||
{"type":"action","name":"take_photos","params":{"target_class":"window","description":"open window","track_time":5.0}}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{"type":"action","name":"loiter","params":{"duration":0.1}} // 占位动作
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// 移动到B点...
|
|
||||||
{
|
|
||||||
"type": "Parallel",
|
|
||||||
"name": "FlyAndInspectToB",
|
|
||||||
"children": [
|
|
||||||
{"type":"action","name":"fly_to_waypoint","params":{"x":-108.5,"y":241.8,"z":12.0}},
|
|
||||||
{
|
|
||||||
"type": "Selector",
|
|
||||||
"name": "OpportunisticPhoto",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "decorator",
|
|
||||||
"name": "SuccessIsFailure",
|
|
||||||
"child": {
|
|
||||||
"type": "Sequence",
|
|
||||||
"name": "DetectAndCapture",
|
|
||||||
"children": [
|
|
||||||
{"type":"action","name":"object_detect","params":{"target_class":"window","description":"open window"}},
|
|
||||||
{"type":"condition","name":"object_detected","params":{"target_class":"window","description":"open window"}},
|
|
||||||
{"type":"action","name":"take_photos","params":{"target_class":"window","description":"open window","track_time":5.0}}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{"type":"action","name":"loiter","params":{"duration":0.1}}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// 移动到C点...
|
|
||||||
{
|
|
||||||
"type": "Parallel",
|
|
||||||
"name": "FlyAndInspectToC",
|
|
||||||
"children": [
|
|
||||||
{"type":"action","name":"fly_to_waypoint","params":{"x":-108.5,"y":289.8,"z":12.0}},
|
|
||||||
{
|
|
||||||
"type": "Selector",
|
|
||||||
"name": "OpportunisticPhoto",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "decorator",
|
|
||||||
"name": "SuccessIsFailure",
|
|
||||||
"child": {
|
|
||||||
"type": "Sequence",
|
|
||||||
"name": "DetectAndCapture",
|
|
||||||
"children": [
|
|
||||||
{"type":"action","name":"object_detect","params":{"target_class":"window","description":"open window"}},
|
|
||||||
{"type":"condition","name":"object_detected","params":{"target_class":"window","description":"open window"}},
|
|
||||||
{"type":"action","name":"take_photos","params":{"target_class":"window","description":"open window","track_time":5.0}}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{"type":"action","name":"loiter","params":{"duration":0.1}}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// 移动到D点...
|
|
||||||
{
|
|
||||||
"type": "Parallel",
|
|
||||||
"name": "FlyAndInspectToD",
|
|
||||||
"children": [
|
|
||||||
{"type":"action","name":"fly_to_waypoint","params":{"x":-24.0,"y":292.8,"z":12.0}},
|
|
||||||
{
|
|
||||||
"type": "Selector",
|
|
||||||
"name": "OpportunisticPhoto",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "decorator",
|
|
||||||
"name": "SuccessIsFailure",
|
|
||||||
"child": {
|
|
||||||
"type": "Sequence",
|
|
||||||
"name": "DetectAndCapture",
|
|
||||||
"children": [
|
|
||||||
{"type":"action","name":"object_detect","params":{"target_class":"window","description":"open window"}},
|
|
||||||
{"type":"condition","name":"object_detected","params":{"target_class":"window","description":"open window"}},
|
|
||||||
{"type":"action","name":"take_photos","params":{"target_class":"window","description":"open window","track_time":5.0}}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{"type":"action","name":"loiter","params":{"duration":0.1}}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 五、优先级排序任务通用示例
|
|
||||||
当用户指令中明确提出有多个待考察且具有优先级关系的物体时,节点描述须为优先级关系。
|
|
||||||
| 用户指令场景 | `target_class` | `description` |
|
|
||||||
|-----------------------------|-----------------|-------------------------|
|
|
||||||
| 红气球>蓝气球>绿气球 | `balloon` | `(红>蓝>绿)` |
|
|
||||||
| 军用卡车>民用卡车>面包车 | `truck` | `(军用卡车>民用卡车>面包车)` |
|
|
||||||
|
|
||||||
## 六、高频错误规避
|
## 五、高频错误规避(确保验证通过)
|
||||||
1. 优先级排序不可修改`target_class`,仅用`description`填排序规则;
|
1. 优先级排序不可修改`target_class`:如"民用卡车、面包车与军用卡车中,军用卡车优先",`target_class`仍为`truck`,仅用`description`填排序规则;
|
||||||
2. `track_object`必传`track_time`;
|
2. 在没有明确指出物体之间的优先级关系情况下,`description`字段只描述物体属性本身,严禁与用户指令中不存在的物体进行排序;
|
||||||
3. `gps_status`的`min_satellites`必须在6-15之间;
|
3. `track_object`必传`track_time`:不可用`duration`替代(如跟踪30秒填`"track_time":30.0`);
|
||||||
4. 严禁输出 markdown 代码块标记,直接输出 JSON 纯文本;
|
4. 无自定义节点:"锁定高优先级目标"需通过`object_detect`+`object_detected`实现,不可用"lock_high_risk_target"。
|
||||||
5. 控制流节点的 `type` 必须是 `"Sequence"`, `"Selector"` 或 `"Parallel"`;
|
|
||||||
6. rotate与rotate_search动作节点意思是无人机以自身为原点旋转,而非围绕外部点旋转
|
|
||||||
7. 当用户指令中要求执行动作前增加人工确认时,比如“我确认后拍照”,则必须在拍照动作前增加manual_confirmation节点
|
|
||||||
|
|
||||||
## 七、输出要求
|
|
||||||
仅输出1个严格符合上述所有规则的JSON对象。
|
## 六、输出要求
|
||||||
|
仅输出1个严格符合上述所有规则的JSON对象,**确保:1. 优先级排序逻辑正确填入`description`;2. `target_class`匹配预定义列表;3. 行为树结构不变;4. 后端解析与Schema验证无错误**,无任何冗余内容。
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ def _parse_allowed_nodes_from_prompt(prompt_text: str) -> tuple[Set[str], Set[st
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# 使用更精确的正则表达式匹配节点定义部分
|
# 使用更精确的正则表达式匹配节点定义部分
|
||||||
node_section_pattern = r"#### 1\. 可用节点定义.*?```json\s*({.*?})\s*```"
|
node_section_pattern = r"#### 2\. 可用节点定义.*?```json\s*({.*?})\s*```"
|
||||||
match = re.search(node_section_pattern, prompt_text, re.DOTALL | re.IGNORECASE)
|
match = re.search(node_section_pattern, prompt_text, re.DOTALL | re.IGNORECASE)
|
||||||
|
|
||||||
if not match:
|
if not match:
|
||||||
@@ -144,12 +144,25 @@ def _fallback_parse_nodes(prompt_text: str) -> tuple[Set[str], Set[str]]:
|
|||||||
logging.error("在所有JSON代码块中都没有找到有效的节点定义结构。")
|
logging.error("在所有JSON代码块中都没有找到有效的节点定义结构。")
|
||||||
return set(), set()
|
return set(), set()
|
||||||
|
|
||||||
|
def _find_nodes_by_name(node: Dict, target_name: str) -> List[Dict]:
|
||||||
|
"""递归查找所有指定名称的节点"""
|
||||||
|
nodes_found = []
|
||||||
|
|
||||||
|
if node.get("name") == target_name:
|
||||||
|
nodes_found.append(node)
|
||||||
|
|
||||||
|
# 递归搜索子节点
|
||||||
|
for child in node.get("children", []):
|
||||||
|
nodes_found.extend(_find_nodes_by_name(child, target_name))
|
||||||
|
|
||||||
|
return nodes_found
|
||||||
|
|
||||||
def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> dict:
|
def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> dict:
|
||||||
"""
|
"""
|
||||||
根据允许的行动和条件节点,动态生成一个JSON Schema。
|
根据允许的行动和条件节点,动态生成一个JSON Schema。
|
||||||
"""
|
"""
|
||||||
# 所有可能的节点类型
|
# 所有可能的节点类型
|
||||||
node_types = ["action", "condition", "Sequence", "Selector", "Parallel", "decorator"]
|
node_types = ["action", "condition", "Sequence", "Selector", "Parallel"]
|
||||||
|
|
||||||
# 目标检测相关的类别枚举
|
# 目标检测相关的类别枚举
|
||||||
target_classes = [
|
target_classes = [
|
||||||
@@ -162,44 +175,38 @@ def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> di
|
|||||||
"sandwich", "orange", "broccoli", "carrot", "hot_dog", "pizza", "donut", "cake", "chair",
|
"sandwich", "orange", "broccoli", "carrot", "hot_dog", "pizza", "donut", "cake", "chair",
|
||||||
"couch", "potted_plant", "bed", "dining_table", "toilet", "tv", "laptop", "mouse", "remote",
|
"couch", "potted_plant", "bed", "dining_table", "toilet", "tv", "laptop", "mouse", "remote",
|
||||||
"keyboard", "cell_phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book",
|
"keyboard", "cell_phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book",
|
||||||
"clock", "vase", "scissors", "teddy_bear", "hair_drier", "toothbrush","balloon","trash","window","garbage"
|
"clock", "vase", "scissors", "teddy_bear", "hair_drier", "toothbrush","balloon"
|
||||||
]
|
]
|
||||||
|
|
||||||
# 递归节点定义
|
# 递归节点定义
|
||||||
node_definition = {
|
node_definition = {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
# 修改:手动构造不区分大小写的正则,避免使用不支持的 (?i) 标志
|
"type": {"type": "string", "enum": node_types},
|
||||||
# 匹配: action, condition, sequence, selector, parallel, decorator (忽略大小写)
|
|
||||||
"type": {
|
|
||||||
"type": "string",
|
|
||||||
"pattern": "^([Aa][Cc][Tt][Ii][Oo][Nn]|[Cc][Oo][Nn][Dd][Ii][Tt][Ii][Oo][Nn]|[Ss][Ee][Qq][Uu][Ee][Nn][Cc][Ee]|[Ss][Ee][Ll][Ee][Cc][Tt][Oo][Rr]|[Pp][Aa][Rr][Aa][Ll][Ll][Ee][Ll]|[Dd][Ee][Cc][Oo][Rr][Aa][Tt][Oo][Rr])$"
|
|
||||||
},
|
|
||||||
"name": {"type": "string"},
|
"name": {"type": "string"},
|
||||||
"params": {"type": "object"},
|
"params": {"type": "object"},
|
||||||
"children": {
|
"children": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {"$ref": "#/definitions/node"}
|
"items": {"$ref": "#/definitions/node"}
|
||||||
},
|
}
|
||||||
"child": {"$ref": "#/definitions/node"}
|
|
||||||
},
|
},
|
||||||
"required": ["type", "name"],
|
"required": ["type", "name"],
|
||||||
"allOf": [
|
"allOf": [
|
||||||
# 动作节点验证 (忽略大小写)
|
# 动作节点验证
|
||||||
{
|
{
|
||||||
"if": {"properties": {"type": {"pattern": "^[Aa][Cc][Tt][Ii][Oo][Nn]$"}}},
|
"if": {"properties": {"type": {"const": "action"}}},
|
||||||
"then": {"properties": {"name": {"enum": sorted(list(allowed_actions))}}}
|
"then": {"properties": {"name": {"enum": sorted(list(allowed_actions))}}}
|
||||||
},
|
},
|
||||||
# 条件节点验证 (忽略大小写)
|
# 条件节点验证
|
||||||
{
|
{
|
||||||
"if": {"properties": {"type": {"pattern": "^[Cc][Oo][Nn][Dd][Ii][Tt][Ii][Oo][Nn]$"}}},
|
"if": {"properties": {"type": {"const": "condition"}}},
|
||||||
"then": {"properties": {"name": {"enum": sorted(list(allowed_conditions))}}}
|
"then": {"properties": {"name": {"enum": sorted(list(allowed_conditions))}}}
|
||||||
},
|
},
|
||||||
# 目标检测动作节点的参数验证 (忽略大小写)
|
# 目标检测动作节点的参数验证
|
||||||
{
|
{
|
||||||
"if": {
|
"if": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"type": {"pattern": "^[Aa][Cc][Tt][Ii][Oo][Nn]$"},
|
"type": {"const": "action"},
|
||||||
"name": {"const": "object_detect"}
|
"name": {"const": "object_detect"}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -218,11 +225,11 @@ def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> di
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
# 目标检测条件节点的参数验证 (忽略大小写)
|
# 目标检测条件节点的参数验证
|
||||||
{
|
{
|
||||||
"if": {
|
"if": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"type": {"pattern": "^[Cc][Oo][Nn][Dd][Ii][Tt][Ii][Oo][Nn]$"},
|
"type": {"const": "condition"},
|
||||||
"name": {"const": "object_detected"}
|
"name": {"const": "object_detected"}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -240,48 +247,6 @@ def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> di
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
# 电池监控节点的参数验证 (忽略大小写)
|
|
||||||
{
|
|
||||||
"if": {
|
|
||||||
"properties": {
|
|
||||||
"type": {"pattern": "^[Cc][Oo][Nn][Dd][Ii][Tt][Ii][Oo][Nn]$"},
|
|
||||||
"name": {"const": "battery_above"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"then": {
|
|
||||||
"properties": {
|
|
||||||
"params": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"threshold": {"type": "number", "minimum": 0.0, "maximum": 1.0}
|
|
||||||
},
|
|
||||||
"required": ["threshold"],
|
|
||||||
"additionalProperties": False
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
# GPS状态节点的参数验证 (忽略大小写)
|
|
||||||
{
|
|
||||||
"if": {
|
|
||||||
"properties": {
|
|
||||||
"type": {"pattern": "^[Cc][Oo][Nn][Dd][Ii][Tt][Ii][Oo][Nn]$"},
|
|
||||||
"name": {"const": "gps_status"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"then": {
|
|
||||||
"properties": {
|
|
||||||
"params": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"min_satellites": {"type": "integer", "minimum": 6, "maximum": 15}
|
|
||||||
},
|
|
||||||
"required": ["min_satellites"],
|
|
||||||
"additionalProperties": False
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -338,7 +303,6 @@ def _validate_pytree_with_schema(pytree_instance: dict, schema: dict) -> bool:
|
|||||||
try:
|
try:
|
||||||
jsonschema.validate(instance=pytree_instance, schema=schema)
|
jsonschema.validate(instance=pytree_instance, schema=schema)
|
||||||
logging.info("✅ JSON Schema验证成功")
|
logging.info("✅ JSON Schema验证成功")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
except jsonschema.ValidationError as e:
|
except jsonschema.ValidationError as e:
|
||||||
logging.warning("❌ Pytree验证失败")
|
logging.warning("❌ Pytree验证失败")
|
||||||
@@ -349,10 +313,6 @@ def _validate_pytree_with_schema(pytree_instance: dict, schema: dict) -> bool:
|
|||||||
# 提供更具体的错误信息
|
# 提供更具体的错误信息
|
||||||
if "object_detect" in str(e.message) or "object_detected" in str(e.message):
|
if "object_detect" in str(e.message) or "object_detected" in str(e.message):
|
||||||
logging.warning("💡 提示: 请确保目标类别是预定义列表中的有效值")
|
logging.warning("💡 提示: 请确保目标类别是预定义列表中的有效值")
|
||||||
elif "battery_above" in str(e.message):
|
|
||||||
logging.warning("💡 提示: 电池阈值必须在0.0到1.0之间")
|
|
||||||
elif "gps_status" in str(e.message):
|
|
||||||
logging.warning("💡 提示: 最小卫星数量必须在6到15之间")
|
|
||||||
|
|
||||||
return False
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -374,10 +334,10 @@ def _visualize_pytree(node: Dict, file_path: str):
|
|||||||
|
|
||||||
# 选择合适的中文字体,避免中文乱码
|
# 选择合适的中文字体,避免中文乱码
|
||||||
def _pick_zh_font():
|
def _pick_zh_font():
|
||||||
sys = platform.system()
|
system = platform.system()
|
||||||
if sys == "Windows":
|
if system == "Windows":
|
||||||
return "Microsoft YaHei"
|
return "Microsoft YaHei"
|
||||||
elif sys == "Darwin":
|
elif system == "Darwin":
|
||||||
return "PingFang SC"
|
return "PingFang SC"
|
||||||
else:
|
else:
|
||||||
return "Noto Sans CJK SC"
|
return "Noto Sans CJK SC"
|
||||||
@@ -407,7 +367,19 @@ def _visualize_pytree(node: Dict, file_path: str):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("❌ 生成可视化图形失败")
|
logging.error("❌ 生成可视化图形失败")
|
||||||
logging.error("请确保您的系统已经正确安装了Graphviz图形库。")
|
logging.error("请确保您的系统已经正确安装了Graphviz图形库。")
|
||||||
|
logging.error("安装方法:")
|
||||||
|
logging.error(" Ubuntu/Debian: sudo apt-get install graphviz")
|
||||||
|
logging.error(" CentOS/RHEL: sudo yum install graphviz")
|
||||||
|
logging.error(" macOS: brew install graphviz")
|
||||||
logging.error(f"错误详情: {e}")
|
logging.error(f"错误详情: {e}")
|
||||||
|
# 清理可能残留的源文件
|
||||||
|
try:
|
||||||
|
gv_file = f"{render_path}.gv"
|
||||||
|
if os.path.exists(gv_file):
|
||||||
|
os.remove(gv_file)
|
||||||
|
logging.info(f"已清理残留的源文件: {gv_file}")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
def _add_nodes_and_edges(node: dict, dot, parent_id: str | None = None) -> str:
|
def _add_nodes_and_edges(node: dict, dot, parent_id: str | None = None) -> str:
|
||||||
"""递归辅助函数,用于添加节点和边。"""
|
"""递归辅助函数,用于添加节点和边。"""
|
||||||
@@ -467,15 +439,6 @@ def _add_nodes_and_edges(node: dict, dot, parent_id: str | None = None) -> str:
|
|||||||
shape = 'ellipse'
|
shape = 'ellipse'
|
||||||
style = 'filled'
|
style = 'filled'
|
||||||
fillcolor = '#e1d5e7' # 紫色
|
fillcolor = '#e1d5e7' # 紫色
|
||||||
elif node_type == 'decorator':
|
|
||||||
shape = 'doubleoctagon'
|
|
||||||
style = 'filled'
|
|
||||||
fillcolor = '#f8cecc' # 浅红
|
|
||||||
|
|
||||||
# 特别标记安全相关节点
|
|
||||||
if node.get('name') in ['battery_above', 'gps_status', 'SafetyMonitor']:
|
|
||||||
border_color = '#ff0000' # 红色边框突出显示安全节点
|
|
||||||
style = 'filled,bold' # 加粗
|
|
||||||
|
|
||||||
dot.node(current_id, label=node_label, shape=shape, style=style, fillcolor=fillcolor, color=border_color)
|
dot.node(current_id, label=node_label, shape=shape, style=style, fillcolor=fillcolor, color=border_color)
|
||||||
|
|
||||||
@@ -483,33 +446,28 @@ def _add_nodes_and_edges(node: dict, dot, parent_id: str | None = None) -> str:
|
|||||||
if parent_id:
|
if parent_id:
|
||||||
dot.edge(parent_id, current_id)
|
dot.edge(parent_id, current_id)
|
||||||
|
|
||||||
# 递归处理子节点 (Sequence, Selector, Parallel 等)
|
# 递归处理子节点
|
||||||
children = node.get("children", [])
|
children = node.get("children", [])
|
||||||
|
if not children:
|
||||||
|
return current_id
|
||||||
|
|
||||||
# 兼容 decorator 类型的 child 字段 (处理为单元素列表以便统一逻辑)
|
# 记录所有子节点的ID
|
||||||
if node_type == 'decorator' and 'child' in node:
|
child_ids = []
|
||||||
children = [node['child']]
|
|
||||||
|
|
||||||
if children:
|
# 正确的递归连接:每个子节点都连接到当前节点
|
||||||
# 记录所有子节点的ID
|
for child in children:
|
||||||
child_ids = []
|
child_id = _add_nodes_and_edges(child, dot, current_id)
|
||||||
|
child_ids.append(child_id)
|
||||||
|
|
||||||
# 正确的递归连接:每个子节点都连接到当前节点
|
# 子节点同级排列(横向排布,更直观地表现同层)
|
||||||
for child in children:
|
if len(child_ids) > 1:
|
||||||
child_id = _add_nodes_and_edges(child, dot, current_id)
|
with dot.subgraph(name=f"rank_{current_id}") as s:
|
||||||
child_ids.append(child_id)
|
s.attr(rank='same')
|
||||||
|
for cid in child_ids:
|
||||||
|
s.node(cid)
|
||||||
|
|
||||||
# 子节点同级排列(横向排布,更直观地表现同层)
|
# 行为树中,所有类型的节点都只是父连子,不需要子节点间的额外连接
|
||||||
if len(child_ids) > 1:
|
# Sequence、Selector、Parallel 的执行逻辑由行为树引擎处理,不需要在可视化中体现
|
||||||
with dot.subgraph(name=f"rank_{current_id}") as s:
|
|
||||||
s.attr(rank='same')
|
|
||||||
for cid in child_ids:
|
|
||||||
s.node(cid)
|
|
||||||
|
|
||||||
# 递归处理单子节点 (Decorator) - 已合并到 children 处理逻辑中,此处删除旧逻辑
|
|
||||||
# child = node.get("child")
|
|
||||||
# if child:
|
|
||||||
# _add_nodes_and_edges(child, dot, current_id)
|
|
||||||
|
|
||||||
return current_id
|
return current_id
|
||||||
|
|
||||||
@@ -561,7 +519,7 @@ class PyTreeGenerator:
|
|||||||
self.complex_llm_client = openai.OpenAI(api_key=self.api_key, base_url=self.complex_base_url)
|
self.complex_llm_client = openai.OpenAI(api_key=self.api_key, base_url=self.complex_base_url)
|
||||||
|
|
||||||
# --- ChromaDB Client Setup ---
|
# --- ChromaDB Client Setup ---
|
||||||
vector_store_path = os.path.abspath(os.path.join(self.base_dir, '..', '..', 'tools', 'rag','vector_store'))
|
vector_store_path = os.path.abspath(os.path.join(self.base_dir, '..', '..', 'tools', 'vector_store'))
|
||||||
self.chroma_client = chromadb.PersistentClient(path=vector_store_path)
|
self.chroma_client = chromadb.PersistentClient(path=vector_store_path)
|
||||||
|
|
||||||
# Explicitly use the remote embedding function for queries
|
# Explicitly use the remote embedding function for queries
|
||||||
@@ -698,7 +656,7 @@ class PyTreeGenerator:
|
|||||||
pytree_str = combined_text if combined_text else (msg_content or "")
|
pytree_str = combined_text if combined_text else (msg_content or "")
|
||||||
raw_full_text_for_logging = pytree_str # 保存完整原文(含 <think>)以便失败时完整打印
|
raw_full_text_for_logging = pytree_str # 保存完整原文(含 <think>)以便失败时完整打印
|
||||||
|
|
||||||
# 提取 <think> 推理链内容(若有)
|
# 提取 <think> 推理链内容(若存在)
|
||||||
reasoning_text = None
|
reasoning_text = None
|
||||||
try:
|
try:
|
||||||
think_match = re.search(r"<think>([\s\S]*?)</think>", pytree_str)
|
think_match = re.search(r"<think>([\s\S]*?)</think>", pytree_str)
|
||||||
@@ -800,7 +758,34 @@ class PyTreeGenerator:
|
|||||||
pytree_dict['final_prompt'] = final_prompt
|
pytree_dict['final_prompt'] = final_prompt
|
||||||
return pytree_dict
|
return pytree_dict
|
||||||
|
|
||||||
# 验证生成的复杂行为树
|
# 复杂模式回退:若模型误返回简单结构(root是单个action),则自动包装为完整行为树
|
||||||
|
if mode == "complex" and isinstance(pytree_dict, dict) and 'root' in pytree_dict:
|
||||||
|
root_node = pytree_dict.get('root', {})
|
||||||
|
# 检查是否是简单结构(root是单个action节点,没有children)
|
||||||
|
if (root_node.get('type') == 'action' and
|
||||||
|
('children' not in root_node or not root_node.get('children'))):
|
||||||
|
try:
|
||||||
|
jsonschema.validate(instance=pytree_dict, schema=self.simple_schema)
|
||||||
|
logging.warning("⚠️ 复杂模式生成了简单结构(单个action),触发自动包装为完整行为树的回退逻辑。")
|
||||||
|
action_name = root_node.get('name')
|
||||||
|
action_params = root_node.get('params') if isinstance(root_node.get('params'), dict) else {}
|
||||||
|
|
||||||
|
main_children = [{"type": "action", "name": action_name, "params": action_params}]
|
||||||
|
if action_name != "land":
|
||||||
|
main_children.append({"type": "action", "name": "land", "params": {"mode": "home"}})
|
||||||
|
|
||||||
|
root_parallel = {
|
||||||
|
"type": "Parallel",
|
||||||
|
"name": "Mission",
|
||||||
|
"params": {"policy": "all_success"},
|
||||||
|
"children": [
|
||||||
|
{"type": "Sequence", "name": "MainTask", "children": main_children}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
pytree_dict = {"root": root_parallel}
|
||||||
|
except jsonschema.ValidationError:
|
||||||
|
# 不符合简单结构,按正常复杂验证继续
|
||||||
|
pass
|
||||||
if _validate_pytree_with_schema(pytree_dict, self.schema):
|
if _validate_pytree_with_schema(pytree_dict, self.schema):
|
||||||
logging.info("✅ 成功生成并验证了Pytree")
|
logging.info("✅ 成功生成并验证了Pytree")
|
||||||
plan_id = str(uuid.uuid4())
|
plan_id = str(uuid.uuid4())
|
||||||
|
|||||||
0
backend_service/venv/bin/Activate.ps1
Normal file → Executable file
0
backend_service/venv/bin/Activate.ps1
Normal file → Executable file
4
backend_service/venv/bin/activate
Normal file → Executable file
4
backend_service/venv/bin/activate
Normal file → Executable file
@@ -41,12 +41,12 @@ case "$(uname)" in
|
|||||||
CYGWIN*|MSYS*|MINGW*)
|
CYGWIN*|MSYS*|MINGW*)
|
||||||
# transform D:\path\to\venv to /d/path/to/venv on MSYS and MINGW
|
# transform D:\path\to\venv to /d/path/to/venv on MSYS and MINGW
|
||||||
# and to /cygdrive/d/path/to/venv on Cygwin
|
# and to /cygdrive/d/path/to/venv on Cygwin
|
||||||
VIRTUAL_ENV=$(cygpath /home/a/DronePlanning/backend_service/venv)
|
VIRTUAL_ENV=$(cygpath /home/huangfukk/DronePlanning/backend_service/venv)
|
||||||
export VIRTUAL_ENV
|
export VIRTUAL_ENV
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
# use the path as-is
|
# use the path as-is
|
||||||
export VIRTUAL_ENV=/home/a/DronePlanning/backend_service/venv
|
export VIRTUAL_ENV=/home/huangfukk/DronePlanning/backend_service/venv
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|||||||
2
backend_service/venv/bin/activate.csh
Normal file → Executable file
2
backend_service/venv/bin/activate.csh
Normal file → Executable file
@@ -9,7 +9,7 @@ alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PA
|
|||||||
# Unset irrelevant variables.
|
# Unset irrelevant variables.
|
||||||
deactivate nondestructive
|
deactivate nondestructive
|
||||||
|
|
||||||
setenv VIRTUAL_ENV /home/a/DronePlanning/backend_service/venv
|
setenv VIRTUAL_ENV /home/huangfukk/DronePlanning/backend_service/venv
|
||||||
|
|
||||||
set _OLD_VIRTUAL_PATH="$PATH"
|
set _OLD_VIRTUAL_PATH="$PATH"
|
||||||
setenv PATH "$VIRTUAL_ENV/"bin":$PATH"
|
setenv PATH "$VIRTUAL_ENV/"bin":$PATH"
|
||||||
|
|||||||
2
backend_service/venv/bin/activate.fish
Normal file → Executable file
2
backend_service/venv/bin/activate.fish
Normal file → Executable file
@@ -33,7 +33,7 @@ end
|
|||||||
# Unset irrelevant variables.
|
# Unset irrelevant variables.
|
||||||
deactivate nondestructive
|
deactivate nondestructive
|
||||||
|
|
||||||
set -gx VIRTUAL_ENV /home/a/DronePlanning/backend_service/venv
|
set -gx VIRTUAL_ENV /home/huangfukk/DronePlanning/backend_service/venv
|
||||||
|
|
||||||
set -gx _OLD_VIRTUAL_PATH $PATH
|
set -gx _OLD_VIRTUAL_PATH $PATH
|
||||||
set -gx PATH "$VIRTUAL_ENV/"bin $PATH
|
set -gx PATH "$VIRTUAL_ENV/"bin $PATH
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from chromadb.cli.cli import app
|
from chromadb.cli.cli import app
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from coloredlogs.cli import main
|
from coloredlogs.cli import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
7
backend_service/venv/bin/dashscope
Executable file
7
backend_service/venv/bin/dashscope
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
|
import sys
|
||||||
|
from dashscope.cli import main
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if sys.argv[0].endswith('.exe'):
|
||||||
|
sys.argv[0] = sys.argv[0][:-4]
|
||||||
|
sys.exit(main())
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from distro.distro import main
|
from distro.distro import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from dotenv.__main__ import cli
|
from dotenv.__main__ import cli
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from numpy.f2py.f2py2e import main
|
from numpy.f2py.f2py2e import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from fastapi.cli import main
|
from fastapi.cli import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from huggingface_hub.cli.hf import main
|
from huggingface_hub.cli.hf import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from httpx import main
|
from httpx import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from humanfriendly.cli import main
|
from humanfriendly.cli import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from isympy import main
|
from isympy import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
7
backend_service/venv/bin/json_repair
Executable file
7
backend_service/venv/bin/json_repair
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
|
import sys
|
||||||
|
from json_repair.__main__ import cli
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if sys.argv[0].endswith('.exe'):
|
||||||
|
sys.argv[0] = sys.argv[0][:-4]
|
||||||
|
sys.exit(cli())
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from jsonschema.cli import main
|
from jsonschema.cli import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from markdown_it.cli.parse import main
|
from markdown_it.cli.parse import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
7
backend_service/venv/bin/mcp
Executable file
7
backend_service/venv/bin/mcp
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
|
import sys
|
||||||
|
from mcp.cli import app
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if sys.argv[0].endswith('.exe'):
|
||||||
|
sys.argv[0] = sys.argv[0][:-4]
|
||||||
|
sys.exit(app())
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from charset_normalizer.cli import cli_detect
|
from charset_normalizer.cli import cli_detect
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from numpy._configtool import main
|
from numpy._configtool import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from onnxruntime.tools.onnxruntime_test import main
|
from onnxruntime.tools.onnxruntime_test import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from openai.cli import main
|
from openai.cli import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
from pip._internal.cli.main import main
|
from pip._internal.cli.main import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
if sys.argv[0].endswith('.exe'):
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
sys.argv[0] = sys.argv[0][:-4]
|
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
from pip._internal.cli.main import main
|
from pip._internal.cli.main import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
if sys.argv[0].endswith('.exe'):
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
sys.argv[0] = sys.argv[0][:-4]
|
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
from pip._internal.cli.main import main
|
from pip._internal.cli.main import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
if sys.argv[0].endswith('.exe'):
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
sys.argv[0] = sys.argv[0][:-4]
|
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from pybase64.__main__ import main
|
from pybase64.__main__ import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from pygments.cmdline import main
|
from pygments.cmdline import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
7
backend_service/venv/bin/pyjson5
Executable file
7
backend_service/venv/bin/pyjson5
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
|
import sys
|
||||||
|
from json5.tool import main
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if sys.argv[0].endswith('.exe'):
|
||||||
|
sys.argv[0] = sys.argv[0][:-4]
|
||||||
|
sys.exit(main())
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from build.__main__ import entrypoint
|
from build.__main__ import entrypoint
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from rsa.cli import decrypt
|
from rsa.cli import decrypt
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from rsa.cli import encrypt
|
from rsa.cli import encrypt
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from rsa.cli import keygen
|
from rsa.cli import keygen
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from rsa.util import private_to_public
|
from rsa.util import private_to_public
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from rsa.cli import sign
|
from rsa.cli import sign
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from rsa.cli import verify
|
from rsa.cli import verify
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
/home/a/miniconda3/bin/python3
|
/home/huangfukk/miniconda3/bin/python3
|
||||||
7
backend_service/venv/bin/shortuuid
Executable file
7
backend_service/venv/bin/shortuuid
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
|
import sys
|
||||||
|
from shortuuid.cli import cli
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if sys.argv[0].endswith('.exe'):
|
||||||
|
sys.argv[0] = sys.argv[0][:-4]
|
||||||
|
sys.exit(cli())
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from huggingface_hub.inference._mcp.cli import app
|
from huggingface_hub.inference._mcp.cli import app
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from tqdm.cli import main
|
from tqdm.cli import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from typer.cli import main
|
from typer.cli import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from uvicorn.main import main
|
from uvicorn.main import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from watchfiles.cli import cli
|
from watchfiles.cli import cli
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from websockets.cli import main
|
from websockets.cli import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/home/a/DronePlanning/backend_service/venv/bin/python3
|
#!/home/huangfukk/DronePlanning/backend_service/venv/bin/python3
|
||||||
import sys
|
import sys
|
||||||
from websocket._wsdump import main
|
from websocket._wsdump import main
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
29
backend_service/venv/lib/python3.13/site-packages/COPYING
Normal file
29
backend_service/venv/lib/python3.13/site-packages/COPYING
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
Copyright (c) 2011, Stavros Korokithakis
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
Neither the name of Stochastic Technologies nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
Authors
|
||||||
|
=======
|
||||||
|
|
||||||
|
``pyjwt`` is currently written and maintained by `Jose Padilla <https://github.com/jpadilla>`_.
|
||||||
|
Originally written and maintained by `Jeff Lindsay <https://github.com/progrium>`_.
|
||||||
|
|
||||||
|
A full list of contributors can be found on GitHub’s `overview <https://github.com/jpadilla/pyjwt/graphs/contributors>`_.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pip
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-2022 José Padilla
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
Metadata-Version: 2.1
|
||||||
|
Name: PyJWT
|
||||||
|
Version: 2.10.1
|
||||||
|
Summary: JSON Web Token implementation in Python
|
||||||
|
Author-email: Jose Padilla <hello@jpadilla.com>
|
||||||
|
License: MIT
|
||||||
|
Project-URL: Homepage, https://github.com/jpadilla/pyjwt
|
||||||
|
Keywords: json,jwt,security,signing,token,web
|
||||||
|
Classifier: Development Status :: 5 - Production/Stable
|
||||||
|
Classifier: Intended Audience :: Developers
|
||||||
|
Classifier: License :: OSI Approved :: MIT License
|
||||||
|
Classifier: Natural Language :: English
|
||||||
|
Classifier: Programming Language :: Python
|
||||||
|
Classifier: Programming Language :: Python :: 3
|
||||||
|
Classifier: Programming Language :: Python :: 3 :: Only
|
||||||
|
Classifier: Programming Language :: Python :: 3.9
|
||||||
|
Classifier: Programming Language :: Python :: 3.10
|
||||||
|
Classifier: Programming Language :: Python :: 3.11
|
||||||
|
Classifier: Programming Language :: Python :: 3.12
|
||||||
|
Classifier: Programming Language :: Python :: 3.13
|
||||||
|
Classifier: Topic :: Utilities
|
||||||
|
Requires-Python: >=3.9
|
||||||
|
Description-Content-Type: text/x-rst
|
||||||
|
License-File: LICENSE
|
||||||
|
License-File: AUTHORS.rst
|
||||||
|
Provides-Extra: crypto
|
||||||
|
Requires-Dist: cryptography>=3.4.0; extra == "crypto"
|
||||||
|
Provides-Extra: dev
|
||||||
|
Requires-Dist: coverage[toml]==5.0.4; extra == "dev"
|
||||||
|
Requires-Dist: cryptography>=3.4.0; extra == "dev"
|
||||||
|
Requires-Dist: pre-commit; extra == "dev"
|
||||||
|
Requires-Dist: pytest<7.0.0,>=6.0.0; extra == "dev"
|
||||||
|
Requires-Dist: sphinx; extra == "dev"
|
||||||
|
Requires-Dist: sphinx-rtd-theme; extra == "dev"
|
||||||
|
Requires-Dist: zope.interface; extra == "dev"
|
||||||
|
Provides-Extra: docs
|
||||||
|
Requires-Dist: sphinx; extra == "docs"
|
||||||
|
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
||||||
|
Requires-Dist: zope.interface; extra == "docs"
|
||||||
|
Provides-Extra: tests
|
||||||
|
Requires-Dist: coverage[toml]==5.0.4; extra == "tests"
|
||||||
|
Requires-Dist: pytest<7.0.0,>=6.0.0; extra == "tests"
|
||||||
|
|
||||||
|
PyJWT
|
||||||
|
=====
|
||||||
|
|
||||||
|
.. image:: https://github.com/jpadilla/pyjwt/workflows/CI/badge.svg
|
||||||
|
:target: https://github.com/jpadilla/pyjwt/actions?query=workflow%3ACI
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/pypi/v/pyjwt.svg
|
||||||
|
:target: https://pypi.python.org/pypi/pyjwt
|
||||||
|
|
||||||
|
.. image:: https://codecov.io/gh/jpadilla/pyjwt/branch/master/graph/badge.svg
|
||||||
|
:target: https://codecov.io/gh/jpadilla/pyjwt
|
||||||
|
|
||||||
|
.. image:: https://readthedocs.org/projects/pyjwt/badge/?version=stable
|
||||||
|
:target: https://pyjwt.readthedocs.io/en/stable/
|
||||||
|
|
||||||
|
A Python implementation of `RFC 7519 <https://tools.ietf.org/html/rfc7519>`_. Original implementation was written by `@progrium <https://github.com/progrium>`_.
|
||||||
|
|
||||||
|
Sponsor
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. |auth0-logo| image:: https://github.com/user-attachments/assets/ee98379e-ee76-4bcb-943a-e25c4ea6d174
|
||||||
|
:width: 160px
|
||||||
|
|
||||||
|
+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| |auth0-logo| | If you want to quickly add secure token-based authentication to Python projects, feel free to check Auth0's Python SDK and free plan at `auth0.com/signup <https://auth0.com/signup?utm_source=external_sites&utm_medium=pyjwt&utm_campaign=devn_signup>`_. |
|
||||||
|
+--------------+-----------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
Installing
|
||||||
|
----------
|
||||||
|
|
||||||
|
Install with **pip**:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ pip install PyJWT
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
.. code-block:: pycon
|
||||||
|
|
||||||
|
>>> import jwt
|
||||||
|
>>> encoded = jwt.encode({"some": "payload"}, "secret", algorithm="HS256")
|
||||||
|
>>> print(encoded)
|
||||||
|
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg
|
||||||
|
>>> jwt.decode(encoded, "secret", algorithms=["HS256"])
|
||||||
|
{'some': 'payload'}
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
View the full docs online at https://pyjwt.readthedocs.io/en/stable/
|
||||||
|
|
||||||
|
|
||||||
|
Tests
|
||||||
|
-----
|
||||||
|
|
||||||
|
You can run tests from the project root after cloning with:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ tox
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
PyJWT-2.10.1.dist-info/AUTHORS.rst,sha256=klzkNGECnu2_VY7At89_xLBF3vUSDruXk3xwgUBxzwc,322
|
||||||
|
PyJWT-2.10.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||||
|
PyJWT-2.10.1.dist-info/LICENSE,sha256=eXp6ICMdTEM-nxkR2xcx0GtYKLmPSZgZoDT3wPVvXOU,1085
|
||||||
|
PyJWT-2.10.1.dist-info/METADATA,sha256=EkewF6D6KU8SGaaQzVYfxUUU1P_gs_dp1pYTkoYvAx8,3990
|
||||||
|
PyJWT-2.10.1.dist-info/RECORD,,
|
||||||
|
PyJWT-2.10.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
||||||
|
PyJWT-2.10.1.dist-info/top_level.txt,sha256=RP5DHNyJbMq2ka0FmfTgoSaQzh7e3r5XuCWCO8a00k8,4
|
||||||
|
jwt/__init__.py,sha256=VB2vFKuboTjcDGeZ8r-UqK_dz3NsQSQEqySSICby8Xg,1711
|
||||||
|
jwt/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
jwt/__pycache__/algorithms.cpython-313.pyc,,
|
||||||
|
jwt/__pycache__/api_jwk.cpython-313.pyc,,
|
||||||
|
jwt/__pycache__/api_jws.cpython-313.pyc,,
|
||||||
|
jwt/__pycache__/api_jwt.cpython-313.pyc,,
|
||||||
|
jwt/__pycache__/exceptions.cpython-313.pyc,,
|
||||||
|
jwt/__pycache__/help.cpython-313.pyc,,
|
||||||
|
jwt/__pycache__/jwk_set_cache.cpython-313.pyc,,
|
||||||
|
jwt/__pycache__/jwks_client.cpython-313.pyc,,
|
||||||
|
jwt/__pycache__/types.cpython-313.pyc,,
|
||||||
|
jwt/__pycache__/utils.cpython-313.pyc,,
|
||||||
|
jwt/__pycache__/warnings.cpython-313.pyc,,
|
||||||
|
jwt/algorithms.py,sha256=cKr-XEioe0mBtqJMCaHEswqVOA1Z8Purt5Sb3Bi-5BE,30409
|
||||||
|
jwt/api_jwk.py,sha256=6F1r7rmm8V5qEnBKA_xMjS9R7VoANe1_BL1oD2FrAjE,4451
|
||||||
|
jwt/api_jws.py,sha256=aM8vzqQf6mRrAw7bRy-Moj_pjWsKSVQyYK896AfMjJU,11762
|
||||||
|
jwt/api_jwt.py,sha256=OGT4hok1l5A6FH_KdcrU5g6u6EQ8B7em0r9kGM9SYgA,14512
|
||||||
|
jwt/exceptions.py,sha256=bUIOJ-v9tjopTLS-FYOTc3kFx5WP5IZt7ksN_HE1G9Q,1211
|
||||||
|
jwt/help.py,sha256=vFdNzjQoAch04XCMYpCkyB2blaqHAGAqQrtf9nSPkdk,1808
|
||||||
|
jwt/jwk_set_cache.py,sha256=hBKmN-giU7-G37L_XKgc_OZu2ah4wdbj1ZNG_GkoSE8,959
|
||||||
|
jwt/jwks_client.py,sha256=p9b-IbQqo2tEge9Zit3oSPBFNePqwho96VLbnUrHUWs,4259
|
||||||
|
jwt/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
jwt/types.py,sha256=VnhGv_VFu5a7_mrPoSCB7HaNLrJdhM8Sq1sSfEg0gLU,99
|
||||||
|
jwt/utils.py,sha256=hxOjvDBheBYhz-RIPiEz7Q88dSUSTMzEdKE_Ww2VdJw,3640
|
||||||
|
jwt/warnings.py,sha256=50XWOnyNsIaqzUJTk6XHNiIDykiL763GYA92MjTKmok,59
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Wheel-Version: 1.0
|
||||||
|
Generator: setuptools (75.6.0)
|
||||||
|
Root-Is-Purelib: true
|
||||||
|
Tag: py3-none-any
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
jwt
|
||||||
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
pip
|
||||||
@@ -0,0 +1,547 @@
|
|||||||
|
Metadata-Version: 2.4
|
||||||
|
Name: agentscope
|
||||||
|
Version: 1.0.7
|
||||||
|
Summary: AgentScope: A Flexible yet Robust Multi-Agent Platform.
|
||||||
|
Author-email: SysML team of Alibaba Tongyi Lab <gaodawei.gdw@alibaba-inc.com>
|
||||||
|
License-Expression: Apache-2.0
|
||||||
|
Project-URL: Homepage, https://github.com/agentscope-ai/agentscope
|
||||||
|
Project-URL: Documentation, https://doc.agentscope.io/
|
||||||
|
Project-URL: Repository, https://github.com/agentscope-ai/agentscope
|
||||||
|
Keywords: deep-learning,multi agents,agents
|
||||||
|
Classifier: Development Status :: 4 - Beta
|
||||||
|
Classifier: Programming Language :: Python :: 3
|
||||||
|
Classifier: Programming Language :: Python :: 3.10
|
||||||
|
Classifier: Operating System :: OS Independent
|
||||||
|
Classifier: Intended Audience :: Developers
|
||||||
|
Classifier: Intended Audience :: Science/Research
|
||||||
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
||||||
|
Requires-Python: >=3.10
|
||||||
|
Description-Content-Type: text/markdown
|
||||||
|
License-File: LICENSE
|
||||||
|
Requires-Dist: aioitertools
|
||||||
|
Requires-Dist: anthropic
|
||||||
|
Requires-Dist: dashscope
|
||||||
|
Requires-Dist: docstring_parser
|
||||||
|
Requires-Dist: json5
|
||||||
|
Requires-Dist: json_repair
|
||||||
|
Requires-Dist: mcp>=1.13
|
||||||
|
Requires-Dist: numpy
|
||||||
|
Requires-Dist: openai
|
||||||
|
Requires-Dist: python-datauri
|
||||||
|
Requires-Dist: opentelemetry-api
|
||||||
|
Requires-Dist: opentelemetry-sdk
|
||||||
|
Requires-Dist: opentelemetry-exporter-otlp
|
||||||
|
Requires-Dist: python-socketio
|
||||||
|
Requires-Dist: shortuuid
|
||||||
|
Requires-Dist: tiktoken
|
||||||
|
Requires-Dist: sounddevice
|
||||||
|
Provides-Extra: full
|
||||||
|
Requires-Dist: ollama>=0.5.4; extra == "full"
|
||||||
|
Requires-Dist: google-genai; extra == "full"
|
||||||
|
Requires-Dist: Pillow; extra == "full"
|
||||||
|
Requires-Dist: transformers; extra == "full"
|
||||||
|
Requires-Dist: jinja2; extra == "full"
|
||||||
|
Requires-Dist: ray; extra == "full"
|
||||||
|
Requires-Dist: mem0ai; extra == "full"
|
||||||
|
Requires-Dist: packaging; extra == "full"
|
||||||
|
Requires-Dist: pypdf; extra == "full"
|
||||||
|
Requires-Dist: python-docx; extra == "full"
|
||||||
|
Requires-Dist: nltk; extra == "full"
|
||||||
|
Requires-Dist: qdrant-client; extra == "full"
|
||||||
|
Provides-Extra: dev
|
||||||
|
Requires-Dist: agentscope[full]; extra == "dev"
|
||||||
|
Requires-Dist: pre-commit; extra == "dev"
|
||||||
|
Requires-Dist: pytest; extra == "dev"
|
||||||
|
Requires-Dist: sphinx-gallery; extra == "dev"
|
||||||
|
Requires-Dist: furo; extra == "dev"
|
||||||
|
Requires-Dist: myst_parser; extra == "dev"
|
||||||
|
Requires-Dist: matplotlib; extra == "dev"
|
||||||
|
Requires-Dist: pymilvus[milvus_lite]; extra == "dev"
|
||||||
|
Requires-Dist: reme-ai>=0.1.10.7; python_full_version >= "3.12" and extra == "dev"
|
||||||
|
Dynamic: license-file
|
||||||
|
|
||||||
|
[**中文主页**](https://github.com/agentscope-ai/agentscope/blob/main/README_zh.md) | [**Tutorial**](https://doc.agentscope.io/) | [**Roadmap**](https://github.com/agentscope-ai/agentscope/blob/main/docs/roadmap.md) | [**FAQ**](https://doc.agentscope.io/tutorial/faq.html)
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img
|
||||||
|
src="https://img.alicdn.com/imgextra/i1/O1CN01nTg6w21NqT5qFKH1u_!!6000000001621-55-tps-550-550.svg"
|
||||||
|
alt="AgentScope Logo"
|
||||||
|
width="200"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 align="center">AgentScope: Agent-Oriented Programming for Building LLM Applications</h2>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://arxiv.org/abs/2402.14034">
|
||||||
|
<img
|
||||||
|
src="https://img.shields.io/badge/cs.MA-2402.14034-B31C1C?logo=arxiv&logoColor=B31C1C"
|
||||||
|
alt="arxiv"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="https://pypi.org/project/agentscope/">
|
||||||
|
<img
|
||||||
|
src="https://img.shields.io/badge/python-3.10+-blue?logo=python"
|
||||||
|
alt="pypi"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="https://pypi.org/project/agentscope/">
|
||||||
|
<img
|
||||||
|
src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fpypi.org%2Fpypi%2Fagentscope%2Fjson&query=%24.info.version&prefix=v&logo=pypi&label=version"
|
||||||
|
alt="pypi"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="https://doc.agentscope.io/">
|
||||||
|
<img
|
||||||
|
src="https://img.shields.io/badge/Docs-English%7C%E4%B8%AD%E6%96%87-blue?logo=markdown"
|
||||||
|
alt="docs"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="https://agentscope.io/">
|
||||||
|
<img
|
||||||
|
src="https://img.shields.io/badge/GUI-AgentScope_Studio-blue?logo=look&logoColor=green&color=dark-green"
|
||||||
|
alt="workstation"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="./LICENSE">
|
||||||
|
<img
|
||||||
|
src="https://img.shields.io/badge/license-Apache--2.0-black"
|
||||||
|
alt="license"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://trendshift.io/api/badge/repositories/10079" alt="modelscope%2Fagentscope | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## ✨ Why AgentScope?
|
||||||
|
|
||||||
|
Easy for beginners, powerful for experts.
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="./assets/images/agentscope_v1_0822.png" alt="AgentScope Framework" width="80%"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
- **Transparent to Developers**: Transparent is our **FIRST principle**. Prompt engineering, API invocation, agent building, workflow orchestration, all are visible and controllable for developers. No deep encapsulation or implicit magic.
|
||||||
|
- **[Realtime Steering](https://doc.agentscope.io/tutorial/task_agent.html#realtime-steering)**: Native support for realtime interruption and customized handling.
|
||||||
|
- **More Agentic**: Support [agentic tools management](https://doc.agentscope.io/tutorial/task_tool.html), [agentic long-term memory control](https://doc.agentscope.io/tutorial/task_long_term_memory.html) and agentic RAG, etc.
|
||||||
|
- **Model Agnostic**: Programming once, run with all models.
|
||||||
|
- **LEGO-style Agent Building**: All components are **modular** and **independent**.
|
||||||
|
- **Multi-Agent Oriented**: Designed for **multi-agent**, **explicit** message passing and workflow orchestration, NO deep encapsulation.
|
||||||
|
- **Highly Customizable**: Tools, prompt, agent, workflow, third-party libs & visualization, customization is encouraged everywhere.
|
||||||
|
|
||||||
|
Quick overview of important features in **AgentScope 1.0**:
|
||||||
|
|
||||||
|
| Module | Feature | Tutorial |
|
||||||
|
|------------|------------------------------------------------------------------------------------|-------------------------------------------------------------------------|
|
||||||
|
| model | Support async invocation | [Model](https://doc.agentscope.io/tutorial/task_model.html) |
|
||||||
|
| | Support reasoning model | |
|
||||||
|
| | Support streaming/non-streaming returns | |
|
||||||
|
| tool | Support async/sync tool functions | [Tool](https://doc.agentscope.io/tutorial/task_tool.html) |
|
||||||
|
| | Support streaming/non-streaming returns | |
|
||||||
|
| | Support user interruption | |
|
||||||
|
| | Support post-processing | |
|
||||||
|
| | Support group-wise tools management | |
|
||||||
|
| | Support agentic tools management by meta tool | |
|
||||||
|
| MCP | Support streamable HTTP/SSE/StdIO transport | [MCP](https://doc.agentscope.io/tutorial/task_mcp.html) |
|
||||||
|
| | Support both **stateful** and **stateless** mode MCP Client | |
|
||||||
|
| | Support client- & function-level fine-grained control | |
|
||||||
|
| agent | Support async execution | |
|
||||||
|
| | Support parallel tool calls | |
|
||||||
|
| | Support realtime steering interruption and customized handling | |
|
||||||
|
| | Support automatic state management | |
|
||||||
|
| | Support agent-controlled long-term memory | |
|
||||||
|
| | Support agent hooks | |
|
||||||
|
| tracing | Support OpenTelemetry-based tracing in LLM, tools, agent and formatter | [Tracing](https://doc.agentscope.io/tutorial/task_tracing.html) |
|
||||||
|
| | Support connecting to third-party tracing platforms (e.g. Arize-Phoenix, Langfuse) | |
|
||||||
|
| memory | Support long-term memory | [Memory](https://doc.agentscope.io/tutorial/task_long_term_memory.html) |
|
||||||
|
| session | Provide session/application-level automatic state management | [Session](https://doc.agentscope.io/tutorial/task_state.html) |
|
||||||
|
| evaluation | Provide distributed and parallel evaluation | [Evaluation](https://doc.agentscope.io/tutorial/task_eval.html) |
|
||||||
|
| formatter | Support multi-agent prompt formatting with tools API | [Prompt Formatter](https://doc.agentscope.io/tutorial/task_prompt.html) |
|
||||||
|
| | Support truncation-based formatter strategy | |
|
||||||
|
| plan | Support ReAct-based long-term planning | [Plan](https://doc.agentscope.io/tutorial/task_plan.html) |
|
||||||
|
| | Support manual plan specification | |
|
||||||
|
| RAG | Support agentic RAG | [RAG](https://doc.agentscope.io/tutorial/task_rag.html) |
|
||||||
|
| | Support multimodal RAG | |
|
||||||
|
| ... | | |
|
||||||
|
|
||||||
|
## 📢 News
|
||||||
|
- **[2025-11]** [Contributing Guide](./CONTRIBUTING.md) is online now! Welcome to contribute to AgentScope.
|
||||||
|
- **[2025-09]** **RAG** module in AgentScope 1.0 is online now! Check our [tutorial](https://doc.agentscope.io/tutorial/task_rag.html) and [example](https://github.com/agentscope-ai/agentscope/tree/main/examples/functionality/rag) for more details.
|
||||||
|
- **[2025-09]** **Voice agent** is online! `ReActAgent` supports Qwen-Omni and GPT-Audio natively now, check our [new example](https://github.com/agentscope-ai/agentscope/tree/main/examples/agent/voice_agent) and [roadmap](https://github.com/agentscope-ai/agentscope/issues/773).
|
||||||
|
- **[2025-09]** A new powerful 📋**Plan** module is online now! Check out the [tutorial](https://doc.agentscope.io/tutorial/task_plan.html) for more details.
|
||||||
|
- **[2025-09]** **AgentScope Runtime** is open-sourced now! Enabling effective agent deployment with sandboxed tool execution for production-ready AI applications. Check out the [GitHub repo](https://github.com/agentscope-ai/agentscope-runtime).
|
||||||
|
- **[2025-09]** **AgentScope Studio** is open-sourced now! Check out the [GitHub repo](https://github.com/agentscope-ai/agentscope-studio).
|
||||||
|
- **[2025-08]** The new tutorial of v1 is online now! Check out the [tutorial](https://doc.agentscope.io) for more details.
|
||||||
|
- **[2025-08]** 🎉🎉 AgentScope v1 is released now! This version fully embraces the asynchronous execution, providing many new features and improvements. Check out [changelog](https://github.com/agentscope-ai/agentscope/blob/main/docs/changelog.md) for detailed changes.
|
||||||
|
|
||||||
|
## 💬 Contact
|
||||||
|
|
||||||
|
Welcome to join our community on
|
||||||
|
|
||||||
|
| [Discord](https://discord.gg/eYMpfnkG8h) | DingTalk |
|
||||||
|
|----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| <img src="https://gw.alicdn.com/imgextra/i1/O1CN01hhD1mu1Dd3BWVUvxN_!!6000000000238-2-tps-400-400.png" width="100" height="100"> | <img src="https://img.alicdn.com/imgextra/i1/O1CN01LxzZha1thpIN2cc2E_!!6000000005934-2-tps-497-477.png" width="100" height="100"> |
|
||||||
|
|
||||||
|
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||||
|
## 📑 Table of Contents
|
||||||
|
|
||||||
|
- [🚀 Quickstart](#-quickstart)
|
||||||
|
- [💻 Installation](#-installation)
|
||||||
|
- [🛠️ From source](#-from-source)
|
||||||
|
- [🔄 Using uv (recommended for faster installs)](#-using-uv-recommended-for-faster-installs)
|
||||||
|
- [📦 From PyPi](#-from-pypi)
|
||||||
|
- [📝 Example](#-example)
|
||||||
|
- [👋 Hello AgentScope!](#-hello-agentscope)
|
||||||
|
- [🎯 Realtime Steering](#-realtime-steering)
|
||||||
|
- [🛠️ Fine-Grained MCP Control](#-fine-grained-mcp-control)
|
||||||
|
- [🧑🤝🧑 Multi-Agent Conversation](#-multi-agent-conversation)
|
||||||
|
- [💻 AgentScope Studio](#-agentscope-studio)
|
||||||
|
- [📖 Documentation](#-documentation)
|
||||||
|
- [🤝 Contributing](#-contributing)
|
||||||
|
- [⚖️ License](#-license)
|
||||||
|
- [📚 Publications](#-publications)
|
||||||
|
- [✨ Contributors](#-contributors)
|
||||||
|
|
||||||
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
|
||||||
|
## 🚀 Quickstart
|
||||||
|
|
||||||
|
### 💻 Installation
|
||||||
|
|
||||||
|
> AgentScope requires **Python 3.10** or higher.
|
||||||
|
|
||||||
|
#### 🛠️ From source
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pull the source code from GitHub
|
||||||
|
git clone -b main https://github.com/agentscope-ai/agentscope.git
|
||||||
|
|
||||||
|
# Install the package in editable mode
|
||||||
|
cd agentscope
|
||||||
|
pip install -e .
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 🔄 Using uv (recommended for faster installs)
|
||||||
|
|
||||||
|
[uv](https://github.com/astral-sh/uv) is a fast Python package installer and resolver, written in Rust.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone the repository
|
||||||
|
git clone -b main https://github.com/agentscope-ai/agentscope.git
|
||||||
|
cd agentscope
|
||||||
|
|
||||||
|
# Install with uv
|
||||||
|
uv pip install -e .
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 📦 From PyPi
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install agentscope
|
||||||
|
```
|
||||||
|
|
||||||
|
Or with uv:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv pip install agentscope
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📝 Example
|
||||||
|
|
||||||
|
### 👋 Hello AgentScope!
|
||||||
|
|
||||||
|
Start with a conversation between user and a ReAct agent 🤖 named "Friday"!
|
||||||
|
|
||||||
|
```python
|
||||||
|
from agentscope.agent import ReActAgent, UserAgent
|
||||||
|
from agentscope.model import DashScopeChatModel
|
||||||
|
from agentscope.formatter import DashScopeChatFormatter
|
||||||
|
from agentscope.memory import InMemoryMemory
|
||||||
|
from agentscope.tool import Toolkit, execute_python_code, execute_shell_command
|
||||||
|
import os, asyncio
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
toolkit = Toolkit()
|
||||||
|
toolkit.register_tool_function(execute_python_code)
|
||||||
|
toolkit.register_tool_function(execute_shell_command)
|
||||||
|
|
||||||
|
agent = ReActAgent(
|
||||||
|
name="Friday",
|
||||||
|
sys_prompt="You're a helpful assistant named Friday.",
|
||||||
|
model=DashScopeChatModel(
|
||||||
|
model_name="qwen-max",
|
||||||
|
api_key=os.environ["DASHSCOPE_API_KEY"],
|
||||||
|
stream=True,
|
||||||
|
),
|
||||||
|
memory=InMemoryMemory(),
|
||||||
|
formatter=DashScopeChatFormatter(),
|
||||||
|
toolkit=toolkit,
|
||||||
|
)
|
||||||
|
|
||||||
|
user = UserAgent(name="user")
|
||||||
|
|
||||||
|
msg = None
|
||||||
|
while True:
|
||||||
|
msg = await agent(msg)
|
||||||
|
msg = await user(msg)
|
||||||
|
if msg.get_text_content() == "exit":
|
||||||
|
break
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🎯 Realtime Steering
|
||||||
|
|
||||||
|
Natively support **realtime interruption** in ``ReActAgent`` with robust memory preservation, and convert interruption into an **observable event** for agent to seamlessly resume conversations.
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="./assets/images/realtime_steering_zh.gif" alt="Realtime Steering" width="49%"/>
|
||||||
|
<img src="./assets/images/realtime_steering_en.gif" alt="Realtime Steering" width="49%"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
### 🛠️ Fine-Grained MCP Control
|
||||||
|
|
||||||
|
Developers can obtain the MCP tool as a **local callable function**, and use it anywhere (e.g. call directly, pass to agent, wrap into a more complex tool, etc.)
|
||||||
|
|
||||||
|
```python
|
||||||
|
from agentscope.mcp import HttpStatelessClient
|
||||||
|
from agentscope.tool import Toolkit
|
||||||
|
import os
|
||||||
|
|
||||||
|
async def fine_grained_mcp_control():
|
||||||
|
# Initialize the MCP client
|
||||||
|
client = HttpStatelessClient(
|
||||||
|
name="gaode_mcp",
|
||||||
|
transport="streamable_http",
|
||||||
|
url=f"https://mcp.amap.com/mcp?key={os.environ['GAODE_API_KEY']}",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Obtain the MCP tool as a **local callable function**, and use it anywhere
|
||||||
|
func = await client.get_callable_function(func_name="maps_geo")
|
||||||
|
|
||||||
|
# Option 1: Call directly
|
||||||
|
await func(address="Tiananmen Square", city="Beijing")
|
||||||
|
|
||||||
|
# Option 2: Pass to agent as a tool
|
||||||
|
toolkit = Toolkit()
|
||||||
|
toolkit.register_tool_function(func)
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# Option 3: Wrap into a more complex tool
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🧑🤝🧑 Multi-Agent Conversation
|
||||||
|
|
||||||
|
AgentScope provides ``MsgHub`` and pipelines to streamline multi-agent conversations, offering efficient message routing and seamless information sharing
|
||||||
|
|
||||||
|
```python
|
||||||
|
from agentscope.pipeline import MsgHub, sequential_pipeline
|
||||||
|
from agentscope.message import Msg
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
async def multi_agent_conversation():
|
||||||
|
# Create agents
|
||||||
|
agent1 = ...
|
||||||
|
agent2 = ...
|
||||||
|
agent3 = ...
|
||||||
|
agent4 = ...
|
||||||
|
|
||||||
|
# Create a message hub to manage multi-agent conversation
|
||||||
|
async with MsgHub(
|
||||||
|
participants=[agent1, agent2, agent3],
|
||||||
|
announcement=Msg("Host", "Introduce yourselves.", "assistant")
|
||||||
|
) as hub:
|
||||||
|
# Speak in a sequential manner
|
||||||
|
await sequential_pipeline([agent1, agent2, agent3])
|
||||||
|
# Dynamic manage the participants
|
||||||
|
hub.add(agent4)
|
||||||
|
hub.delete(agent3)
|
||||||
|
await hub.broadcast(Msg("Host", "Goodbye!", "assistant"))
|
||||||
|
|
||||||
|
asyncio.run(multi_agent_conversation())
|
||||||
|
```
|
||||||
|
|
||||||
|
### 💻 AgentScope Studio
|
||||||
|
|
||||||
|
Use the following command to install and start AgentScope Studio, to trace and visualize your agent application.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install -g @agentscope/studio
|
||||||
|
|
||||||
|
as_studio
|
||||||
|
```
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img
|
||||||
|
src="./assets/images/home.gif"
|
||||||
|
width="49%"
|
||||||
|
alt="home"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
src="./assets/images/projects.gif"
|
||||||
|
width="49%"
|
||||||
|
alt="projects"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
src="./assets/images/runtime.gif"
|
||||||
|
width="49%"
|
||||||
|
alt="runtime"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
src="./assets/images/friday.gif"
|
||||||
|
width="49%"
|
||||||
|
alt="friday"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
## 📖 Documentation
|
||||||
|
|
||||||
|
- Tutorial
|
||||||
|
- [Installation](https://doc.agentscope.io/tutorial/quickstart_installation.html)
|
||||||
|
- [Key Concepts](https://doc.agentscope.io/tutorial/quickstart_key_concept.html)
|
||||||
|
- [Create Message](https://doc.agentscope.io/tutorial/quickstart_message.html)
|
||||||
|
- [ReAct Agent](https://doc.agentscope.io/tutorial/quickstart_agent.html)
|
||||||
|
- Workflow
|
||||||
|
- [Conversation](https://doc.agentscope.io/tutorial/workflow_conversation.html)
|
||||||
|
- [Multi-Agent Debate](https://doc.agentscope.io/tutorial/workflow_multiagent_debate.html)
|
||||||
|
- [Concurrent Agents](https://doc.agentscope.io/tutorial/workflow_concurrent_agents.html)
|
||||||
|
- [Routing](https://doc.agentscope.io/tutorial/workflow_routing.html)
|
||||||
|
- [Handoffs](https://doc.agentscope.io/tutorial/workflow_handoffs.html)
|
||||||
|
- FAQ
|
||||||
|
- [FAQ](https://doc.agentscope.io/tutorial/faq.html)
|
||||||
|
- Task Guides
|
||||||
|
- [Model](https://doc.agentscope.io/tutorial/task_model.html)
|
||||||
|
- [Prompt Formatter](https://doc.agentscope.io/tutorial/task_prompt.html)
|
||||||
|
- [Tool](https://doc.agentscope.io/tutorial/task_tool.html)
|
||||||
|
- [Memory](https://doc.agentscope.io/tutorial/task_memory.html)
|
||||||
|
- [Long-Term Memory](https://doc.agentscope.io/tutorial/task_long_term_memory.html)
|
||||||
|
- [Agent](https://doc.agentscope.io/tutorial/task_agent.html)
|
||||||
|
- [Pipeline](https://doc.agentscope.io/tutorial/task_pipeline.html)
|
||||||
|
- [Plan](https://doc.agentscope.io/tutorial/task_plan.html)
|
||||||
|
- [State/Session Management](https://doc.agentscope.io/tutorial/task_state.html)
|
||||||
|
- [Agent Hooks](https://doc.agentscope.io/tutorial/task_hook.html)
|
||||||
|
- [MCP](https://doc.agentscope.io/tutorial/task_mcp.html)
|
||||||
|
- [AgentScope Studio](https://doc.agentscope.io/tutorial/task_studio.html)
|
||||||
|
- [Tracing](https://doc.agentscope.io/tutorial/task_tracing.html)
|
||||||
|
- [Evaluation](https://doc.agentscope.io/tutorial/task_eval.html)
|
||||||
|
- [Embedding](https://doc.agentscope.io/tutorial/task_embedding.html)
|
||||||
|
- [Token](https://doc.agentscope.io/tutorial/task_token.html)
|
||||||
|
- API
|
||||||
|
- [API Docs](https://doc.agentscope.io/api/agentscope.html)
|
||||||
|
- [Examples](https://github.com/agentscope-ai/agentscope/tree/main/examples)
|
||||||
|
- Functionality
|
||||||
|
- [MCP](https://github.com/agentscope-ai/agentscope/tree/main/examples/functionality/mcp)
|
||||||
|
- [Plan](https://github.com/agentscope-ai/agentscope/tree/main/examples/functionality/plan)
|
||||||
|
- [Structured Output](https://github.com/agentscope-ai/agentscope/tree/main/examples/functionality/structured_output)
|
||||||
|
- [RAG](https://github.com/agentscope-ai/agentscope/tree/main/examples/functionality/rag)
|
||||||
|
- [Long-Term Memory](https://github.com/agentscope-ai/agentscope/tree/main/examples/functionality/long_term_memory)
|
||||||
|
- [Session with SQLite](https://github.com/agentscope-ai/agentscope/tree/main/examples/functionality/session_with_sqlite)
|
||||||
|
- [Stream Printing Messages](https://github.com/agentscope-ai/agentscope/tree/main/examples/functionality/stream_printing_messages)
|
||||||
|
- Agent
|
||||||
|
- [ReAct Agent](https://github.com/agentscope-ai/agentscope/tree/main/examples/agent/react_agent)
|
||||||
|
- [Voice Agent](https://github.com/agentscope-ai/agentscope/tree/main/examples/agent/voice_agent)
|
||||||
|
- [Deep Research Agent](https://github.com/agentscope-ai/agentscope/tree/main/examples/agent/deep_research_agent)
|
||||||
|
- [Browser-use Agent](https://github.com/agentscope-ai/agentscope/tree/main/examples/agent/browser_agent)
|
||||||
|
- [Meta Planner Agent](https://github.com/agentscope-ai/agentscope/tree/main/examples/agent/meta_planner_agent)
|
||||||
|
- Game
|
||||||
|
- [Nine-player Werewolves](https://github.com/agentscope-ai/agentscope/tree/main/examples/game/werewolves)
|
||||||
|
- Workflow
|
||||||
|
- [Multi-agent Debate](https://github.com/agentscope-ai/agentscope/tree/main/examples/workflows/multiagent_debate)
|
||||||
|
- [Multi-agent Conversation](https://github.com/agentscope-ai/agentscope/tree/main/examples/workflows/multiagent_conversation)
|
||||||
|
- [Multi-agent Concurrent](https://github.com/agentscope-ai/agentscope/tree/main/examples/workflows/multiagent_concurrent)
|
||||||
|
- Evaluation
|
||||||
|
- [ACEBench](https://github.com/agentscope-ai/agentscope/tree/main/examples/evaluation/ace_bench)
|
||||||
|
- Training
|
||||||
|
- [Reinforcement learning (RL) with Trinity-RFT](https://github.com/agentscope-ai/agentscope/tree/main/examples/training/react_agent)
|
||||||
|
|
||||||
|
|
||||||
|
## 🤝 Contributing
|
||||||
|
|
||||||
|
We welcome contributions from the community! Please refer to our [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines
|
||||||
|
on how to contribute.
|
||||||
|
|
||||||
|
## ⚖️ License
|
||||||
|
|
||||||
|
AgentScope is released under Apache License 2.0.
|
||||||
|
|
||||||
|
## 📚 Publications
|
||||||
|
|
||||||
|
If you find our work helpful for your research or application, please cite our papers.
|
||||||
|
|
||||||
|
- [AgentScope 1.0: A Developer-Centric Framework for Building Agentic Applications](https://arxiv.org/abs/2508.16279)
|
||||||
|
|
||||||
|
- [AgentScope: A Flexible yet Robust Multi-Agent Platform](https://arxiv.org/abs/2402.14034)
|
||||||
|
|
||||||
|
```
|
||||||
|
@article{agentscope_v1,
|
||||||
|
author = {
|
||||||
|
Dawei Gao,
|
||||||
|
Zitao Li,
|
||||||
|
Yuexiang Xie,
|
||||||
|
Weirui Kuang,
|
||||||
|
Liuyi Yao,
|
||||||
|
Bingchen Qian,
|
||||||
|
Zhijian Ma,
|
||||||
|
Yue Cui,
|
||||||
|
Haohao Luo,
|
||||||
|
Shen Li,
|
||||||
|
Lu Yi,
|
||||||
|
Yi Yu,
|
||||||
|
Shiqi He,
|
||||||
|
Zhiling Luo,
|
||||||
|
Wenmeng Zhou,
|
||||||
|
Zhicheng Zhang,
|
||||||
|
Xuguang He,
|
||||||
|
Ziqian Chen,
|
||||||
|
Weikai Liao,
|
||||||
|
Farruh Isakulovich Kushnazarov,
|
||||||
|
Yaliang Li,
|
||||||
|
Bolin Ding,
|
||||||
|
Jingren Zhou}
|
||||||
|
title = {AgentScope 1.0: A Developer-Centric Framework for Building Agentic Applications},
|
||||||
|
journal = {CoRR},
|
||||||
|
volume = {abs/2508.16279},
|
||||||
|
year = {2025},
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{agentscope,
|
||||||
|
author = {
|
||||||
|
Dawei Gao,
|
||||||
|
Zitao Li,
|
||||||
|
Xuchen Pan,
|
||||||
|
Weirui Kuang,
|
||||||
|
Zhijian Ma,
|
||||||
|
Bingchen Qian,
|
||||||
|
Fei Wei,
|
||||||
|
Wenhao Zhang,
|
||||||
|
Yuexiang Xie,
|
||||||
|
Daoyuan Chen,
|
||||||
|
Liuyi Yao,
|
||||||
|
Hongyi Peng,
|
||||||
|
Zeyu Zhang,
|
||||||
|
Lin Zhu,
|
||||||
|
Chen Cheng,
|
||||||
|
Hongzhu Shi,
|
||||||
|
Yaliang Li,
|
||||||
|
Bolin Ding,
|
||||||
|
Jingren Zhou}
|
||||||
|
title = {AgentScope: A Flexible yet Robust Multi-Agent Platform},
|
||||||
|
journal = {CoRR},
|
||||||
|
volume = {abs/2402.14034},
|
||||||
|
year = {2024},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## ✨ Contributors
|
||||||
|
|
||||||
|
All thanks to our contributors:
|
||||||
|
|
||||||
|
<a href="https://github.com/agentscope-ai/agentscope/graphs/contributors">
|
||||||
|
<img src="https://contrib.rocks/image?repo=agentscope-ai/agentscope&max=999&columns=12&anon=1" />
|
||||||
|
</a>
|
||||||
@@ -0,0 +1,315 @@
|
|||||||
|
agentscope-1.0.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||||
|
agentscope-1.0.7.dist-info/METADATA,sha256=HD2a1imJIVJ1oFh55nhcZTP0Li5q-9o1ZDg-K73SVh8,25920
|
||||||
|
agentscope-1.0.7.dist-info/RECORD,,
|
||||||
|
agentscope-1.0.7.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
agentscope-1.0.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
||||||
|
agentscope-1.0.7.dist-info/licenses/LICENSE,sha256=QYa8rfNYxgCABgMJdp22sqNScg1zU9gnu1dMYA_umRM,20637
|
||||||
|
agentscope-1.0.7.dist-info/top_level.txt,sha256=uYSDqkiAOqFFfMz9DGnkhn0_nNVwo1uaFYZQikHbWNM,11
|
||||||
|
agentscope/__init__.py,sha256=MkP1MQ4hMFmRQF8qeUUVuBcCGvLS4fa-eqvogl8aj84,3220
|
||||||
|
agentscope/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/__pycache__/_config.cpython-313.pyc,,
|
||||||
|
agentscope/__pycache__/_logging.cpython-313.pyc,,
|
||||||
|
agentscope/__pycache__/_version.cpython-313.pyc,,
|
||||||
|
agentscope/_config.py,sha256=W1Y2lQbTXj1esxwm_sK2yFqztacz-L1OzRJsaei04GM,767
|
||||||
|
agentscope/_logging.py,sha256=XzcUTb7MfRZkc-B3N1IV-jpHaZG0yFirZyfuGa7HOA4,1223
|
||||||
|
agentscope/_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
agentscope/_utils/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/_utils/__pycache__/_common.cpython-313.pyc,,
|
||||||
|
agentscope/_utils/__pycache__/_mixin.cpython-313.pyc,,
|
||||||
|
agentscope/_utils/_common.py,sha256=ghJITFirhNAwTl_ifi9cI6Oc_Fsvlimiv_sxwi4JXZE,8169
|
||||||
|
agentscope/_utils/_mixin.py,sha256=9co2GJyiShSq6HiQ1KwGABr3mfYw2tyvlzCMgI8L0-w,219
|
||||||
|
agentscope/_version.py,sha256=KpA_lRRXOAnkM0n8CqdbV0FanoevNyEft5-YZ-i1-wI,80
|
||||||
|
agentscope/agent/__init__.py,sha256=fxBL1RmxC__kHPRVYkEj03EJ1mYO32vKsbY5wDIV_x8,496
|
||||||
|
agentscope/agent/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/agent/__pycache__/_agent_base.cpython-313.pyc,,
|
||||||
|
agentscope/agent/__pycache__/_agent_meta.cpython-313.pyc,,
|
||||||
|
agentscope/agent/__pycache__/_react_agent.cpython-313.pyc,,
|
||||||
|
agentscope/agent/__pycache__/_react_agent_base.cpython-313.pyc,,
|
||||||
|
agentscope/agent/__pycache__/_user_agent.cpython-313.pyc,,
|
||||||
|
agentscope/agent/__pycache__/_user_input.cpython-313.pyc,,
|
||||||
|
agentscope/agent/_agent_base.py,sha256=gxF0tuQN0XebkWR-uVM-a_RTR6eKoJnJOvJT90UegKI,24299
|
||||||
|
agentscope/agent/_agent_meta.py,sha256=Ilda4Fonq29oeQ3kUDTIs8db655MQqWIhzvxGiGUrSw,5730
|
||||||
|
agentscope/agent/_react_agent.py,sha256=zQKd9L19k3hjJmCRZYp2GjqedHh5-A45k-8xWcGSDoI,28676
|
||||||
|
agentscope/agent/_react_agent_base.py,sha256=haWRx93D7A_zmhVVJLlJ_YD8I2pCM7g77vok761DOug,3707
|
||||||
|
agentscope/agent/_user_agent.py,sha256=50kYNXWSfYE7L0x8D1Vmzsl1-mg20nSaCDYROzEusPI,4219
|
||||||
|
agentscope/agent/_user_input.py,sha256=oXtHQ8XapV0ov_8S40MAt2EWDHmvC-_zAEZsR9lUt8g,13105
|
||||||
|
agentscope/embedding/__init__.py,sha256=aiGzcY4qzX7a-ieseIH86_8zhry5yaII5Wdkg-hKjXY,871
|
||||||
|
agentscope/embedding/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/embedding/__pycache__/_cache_base.cpython-313.pyc,,
|
||||||
|
agentscope/embedding/__pycache__/_dashscope_embedding.cpython-313.pyc,,
|
||||||
|
agentscope/embedding/__pycache__/_dashscope_multimodal_embedding.cpython-313.pyc,,
|
||||||
|
agentscope/embedding/__pycache__/_embedding_base.cpython-313.pyc,,
|
||||||
|
agentscope/embedding/__pycache__/_embedding_response.cpython-313.pyc,,
|
||||||
|
agentscope/embedding/__pycache__/_embedding_usage.cpython-313.pyc,,
|
||||||
|
agentscope/embedding/__pycache__/_file_cache.cpython-313.pyc,,
|
||||||
|
agentscope/embedding/__pycache__/_gemini_embedding.cpython-313.pyc,,
|
||||||
|
agentscope/embedding/__pycache__/_ollama_embedding.cpython-313.pyc,,
|
||||||
|
agentscope/embedding/__pycache__/_openai_embedding.cpython-313.pyc,,
|
||||||
|
agentscope/embedding/_cache_base.py,sha256=QV_D9Q1Rbtts1BporJVomFwaLyG6nsy9GxhLQNxh4lY,1800
|
||||||
|
agentscope/embedding/_dashscope_embedding.py,sha256=r0qbxhs1kfNgPkgWIvcPXKDdKfVFhKKERpqW9DyfRtM,5905
|
||||||
|
agentscope/embedding/_dashscope_multimodal_embedding.py,sha256=XQ3Dx7sXC18fTXW6kkudhMwJKqiVSfrPOJrGeYpXFEg,8682
|
||||||
|
agentscope/embedding/_embedding_base.py,sha256=sgt_GsFGiuzSCt1-gzhLKJ3Cn4usYbGfHqliHIWMZg8,1191
|
||||||
|
agentscope/embedding/_embedding_response.py,sha256=naEqlewn6J5UyteBewwknH6HlZVTo2SRhN714JhOk2U,1093
|
||||||
|
agentscope/embedding/_embedding_usage.py,sha256=Z1q5yPXuGR8BpB7yUxcn-vPfmRP7gqnGmBjsSGC2f1w,579
|
||||||
|
agentscope/embedding/_file_cache.py,sha256=2MZpiurcVgqkCTIq2U_8Z0n_D0I1Z87owXY0K-Fqfvk,7126
|
||||||
|
agentscope/embedding/_gemini_embedding.py,sha256=5WgGi9iJK6OSbCO_2mG_JbgSxUotR44grNo9CF6yaL0,3549
|
||||||
|
agentscope/embedding/_ollama_embedding.py,sha256=XRAicJgcD8DDHHy0W5du_Jsio4JC3L6pqvctexUOFKA,3379
|
||||||
|
agentscope/embedding/_openai_embedding.py,sha256=ZWcyLR_a9DuekdmHkxD93ck6Hk3ZmytNsLYognCFacE,3491
|
||||||
|
agentscope/evaluate/__init__.py,sha256=8FWPPEnQh95u9TxN8Forj8-b-in2a4_yxn_5F-yMeV8,861
|
||||||
|
agentscope/evaluate/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/__pycache__/_benchmark_base.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/__pycache__/_metric_base.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/__pycache__/_solution.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/__pycache__/_task.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_ace_benchmark/__init__.py,sha256=ZtgAiBvYaSJOmUPXX2P5hf_cJdECxJiMgEtyt99pI38,331
|
||||||
|
agentscope/evaluate/_ace_benchmark/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_ace_benchmark/__pycache__/_ace_benchmark.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_ace_benchmark/__pycache__/_ace_metric.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_ace_benchmark/__pycache__/_ace_tools_zh.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_ace_benchmark/_ace_benchmark.py,sha256=8MV9V0FMSV3YfymVKd_ybJRt_dzybKBTJMeivZ393FI,8151
|
||||||
|
agentscope/evaluate/_ace_benchmark/_ace_metric.py,sha256=6_VBBJ50v7aL6OdwJXRRUrqx2gdWtnpM6jlcZMHP6cQ,4225
|
||||||
|
agentscope/evaluate/_ace_benchmark/_ace_tools_api/__init__.py,sha256=DFWSvwmPolmgqEbqwmkvO05mTr2QNoneO_eXFjEw7ac,327
|
||||||
|
agentscope/evaluate/_ace_benchmark/_ace_tools_api/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_ace_benchmark/_ace_tools_api/__pycache__/_food_platform_api.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_ace_benchmark/_ace_tools_api/__pycache__/_message_api.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_ace_benchmark/_ace_tools_api/__pycache__/_reminder_api.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_ace_benchmark/_ace_tools_api/__pycache__/_shared_state.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_ace_benchmark/_ace_tools_api/__pycache__/_travel_api.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_ace_benchmark/_ace_tools_api/_food_platform_api.py,sha256=TUQD6gR2VzjtUDK9pZKoWr-H9U0opKcUjvqRmP3q58c,9871
|
||||||
|
agentscope/evaluate/_ace_benchmark/_ace_tools_api/_message_api.py,sha256=oJcSfj5Kw_BIMI3eC3LsxB5ZI_nN4o_wd0jOX6OXPGA,11784
|
||||||
|
agentscope/evaluate/_ace_benchmark/_ace_tools_api/_reminder_api.py,sha256=ObmgAI4UnpoZmgiRpGvqwXL2Bs5YH1KQG1JVBM9AXmM,7082
|
||||||
|
agentscope/evaluate/_ace_benchmark/_ace_tools_api/_shared_state.py,sha256=sLkWasNv57-YxeMEj-GIj6mB6LN0RmsIpOaL3fjImmg,551
|
||||||
|
agentscope/evaluate/_ace_benchmark/_ace_tools_api/_travel_api.py,sha256=aa5ixysqx6mO3Msueyv_vrJ3yJbdMBKkBmyaKoozJoU,28644
|
||||||
|
agentscope/evaluate/_ace_benchmark/_ace_tools_zh.py,sha256=f10Vu8N82BYe5aBVo8hi2FkC1f0VKMqrNouWEG8DsQM,4052
|
||||||
|
agentscope/evaluate/_benchmark_base.py,sha256=YqFeDn6G-e8njE48Yso5-tRSeE5dZzDRhwvJKCnriQQ,1263
|
||||||
|
agentscope/evaluate/_evaluator/__init__.py,sha256=2g3K8cHhugTwf64ywiF-yLFihpvlrBMX01bUNTQ-yik,280
|
||||||
|
agentscope/evaluate/_evaluator/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_evaluator/__pycache__/_evaluator_base.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_evaluator/__pycache__/_general_evaluator.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_evaluator/__pycache__/_ray_evaluator.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_evaluator/_evaluator_base.py,sha256=Y7LWZDTLmKyiq4WvpLj6dSMJSZw4N80ty-BFEAg5Wxg,7317
|
||||||
|
agentscope/evaluate/_evaluator/_general_evaluator.py,sha256=GSMdijP5a8KPVvc6QrAqOI7pRFyXhd-n3yKekgqSHB0,3817
|
||||||
|
agentscope/evaluate/_evaluator/_ray_evaluator.py,sha256=CS_Hng_j2lY5nlaN5Fb1Uq5hL99dcNyzu2OaGl_HLk0,6075
|
||||||
|
agentscope/evaluate/_evaluator_storage/__init__.py,sha256=bSdhxI83A-2OVMX6L-ZO8EwYytyBQy6XwGIFQbdTod4,262
|
||||||
|
agentscope/evaluate/_evaluator_storage/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_evaluator_storage/__pycache__/_evaluator_storage_base.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_evaluator_storage/__pycache__/_file_evaluator_storage.cpython-313.pyc,,
|
||||||
|
agentscope/evaluate/_evaluator_storage/_evaluator_storage_base.py,sha256=IW4A6WkBGf43uGj59f7Eu5ySTiX1rmPbpjX1iYjCl8A,5459
|
||||||
|
agentscope/evaluate/_evaluator_storage/_file_evaluator_storage.py,sha256=xPBZ6GXCSL3YMJJNsqH0-bzCxo4_FhZDEqLJcGXLexA,11096
|
||||||
|
agentscope/evaluate/_metric_base.py,sha256=ksYBDhjmE8vTv8cMy31HBhbvkGgIYhUBiC8Qw4zpOAE,2663
|
||||||
|
agentscope/evaluate/_solution.py,sha256=CDbGtYOerCd_o2WgKMnFUPrzF51tXeAcMRu2-poyyho,1194
|
||||||
|
agentscope/evaluate/_task.py,sha256=RJw6GbBTZmYN7bZY3DR6qEDKB4bgV3zwNbWt5vZZEhU,1585
|
||||||
|
agentscope/exception/__init__.py,sha256=eaWWszusA4Lm3CaPmyZCpLBkjwxc17IAPq0MioznbQQ,361
|
||||||
|
agentscope/exception/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/exception/__pycache__/_exception_base.cpython-313.pyc,,
|
||||||
|
agentscope/exception/__pycache__/_tool.cpython-313.pyc,,
|
||||||
|
agentscope/exception/_exception_base.py,sha256=o0VAg5seb9GnJNs_eZMrlmxqbI7UEyzwr7B1gwA48oQ,660
|
||||||
|
agentscope/exception/_tool.py,sha256=AsCWmwM7HF5fFKD6JhI6ohRGMZG7i8H1cwWj3xfiPUc,512
|
||||||
|
agentscope/formatter/__init__.py,sha256=mgg0ZCZsk-beTELeVU9HVaU9T38w3u5gMyA4LT-wwag,1184
|
||||||
|
agentscope/formatter/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/formatter/__pycache__/_anthropic_formatter.cpython-313.pyc,,
|
||||||
|
agentscope/formatter/__pycache__/_dashscope_formatter.cpython-313.pyc,,
|
||||||
|
agentscope/formatter/__pycache__/_deepseek_formatter.cpython-313.pyc,,
|
||||||
|
agentscope/formatter/__pycache__/_formatter_base.cpython-313.pyc,,
|
||||||
|
agentscope/formatter/__pycache__/_gemini_formatter.cpython-313.pyc,,
|
||||||
|
agentscope/formatter/__pycache__/_ollama_formatter.cpython-313.pyc,,
|
||||||
|
agentscope/formatter/__pycache__/_openai_formatter.cpython-313.pyc,,
|
||||||
|
agentscope/formatter/__pycache__/_truncated_formatter_base.cpython-313.pyc,,
|
||||||
|
agentscope/formatter/_anthropic_formatter.py,sha256=yJMg6IZX538aKmC6JqYzddhVeLSGz92X9ubjg9MNvaA,8226
|
||||||
|
agentscope/formatter/_dashscope_formatter.py,sha256=FWkX2_zHXCaq4sK_o6NH5b_qcXin_tODDhFqAk0x78A,14548
|
||||||
|
agentscope/formatter/_deepseek_formatter.py,sha256=5Ewi8c99qYL5ZCyE6oKek1x3cqNMfHDtXay43Goohi0,8143
|
||||||
|
agentscope/formatter/_formatter_base.py,sha256=FQKf_3KrydfzuAIL5m5YbC36pfz7lXjnnjG_VI25Cm0,3690
|
||||||
|
agentscope/formatter/_gemini_formatter.py,sha256=n6okME8L623zoLayNXG1uf-UEDgB3xydmpuWipUm4kY,13036
|
||||||
|
agentscope/formatter/_ollama_formatter.py,sha256=fqGKIdhPyMizYSBLj-YwtcbIPBriXolEvrPQuR-ZeiY,10290
|
||||||
|
agentscope/formatter/_openai_formatter.py,sha256=6wrcmO7mqba1X-VWMbYEGjWIv7NfhW-4nctLqvuDaU0,13214
|
||||||
|
agentscope/formatter/_truncated_formatter_base.py,sha256=xKqBL-qiDZSLaHq1ls9fUZZY8xSikdpLqC85hVSWbDM,10211
|
||||||
|
agentscope/hooks/__init__.py,sha256=gn0gD6IUtvlN8fGSy9YZRcS3ANpewvyPjQZuHo2SylU,670
|
||||||
|
agentscope/hooks/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/hooks/__pycache__/_studio_hooks.cpython-313.pyc,,
|
||||||
|
agentscope/hooks/_studio_hooks.py,sha256=fxGjYIwqQ1vs0Yr3zrcfAwO1ZWLyoSvG_0MCmjLnBog,1143
|
||||||
|
agentscope/mcp/__init__.py,sha256=8_x7Loaxt1PVh5h5ubr_RdnyF9wRJOhSsBYMHWaPFRw,587
|
||||||
|
agentscope/mcp/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/mcp/__pycache__/_client_base.cpython-313.pyc,,
|
||||||
|
agentscope/mcp/__pycache__/_http_stateful_client.cpython-313.pyc,,
|
||||||
|
agentscope/mcp/__pycache__/_http_stateless_client.cpython-313.pyc,,
|
||||||
|
agentscope/mcp/__pycache__/_mcp_function.cpython-313.pyc,,
|
||||||
|
agentscope/mcp/__pycache__/_stateful_client_base.cpython-313.pyc,,
|
||||||
|
agentscope/mcp/__pycache__/_stdio_stateful_client.cpython-313.pyc,,
|
||||||
|
agentscope/mcp/_client_base.py,sha256=XzYjrxwSzIEbAVO5IzUlbKTtzdr41fTfBtSUnISpGvY,3274
|
||||||
|
agentscope/mcp/_http_stateful_client.py,sha256=Ekwd1KUJ2RX9oWvv3ZEqDy1FQaXa-hZbSp019K_eR_M,3186
|
||||||
|
agentscope/mcp/_http_stateless_client.py,sha256=LotK9FgzXtSi7VSSGxF0PwYagARBd72P2derYe2Itmg,5223
|
||||||
|
agentscope/mcp/_mcp_function.py,sha256=b_bK9chwTriE7pbncuzG3j46HZkG8BSugvpBr8Z90bI,2604
|
||||||
|
agentscope/mcp/_stateful_client_base.py,sha256=l01VBdAsPNtwR50TnJtwx7NqcIB89V6xpiSN-QUFBeU,5206
|
||||||
|
agentscope/mcp/_stdio_stateful_client.py,sha256=IjDjvha_82RCJgQ8eOitQfPnBVdWzy-ljzzYxnpAgvo,2950
|
||||||
|
agentscope/memory/__init__.py,sha256=PlElb2Rt9lL9JcNZ6ADCvCJwaMPlhPdZIaaBkUlOHfs,555
|
||||||
|
agentscope/memory/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/memory/__pycache__/_in_memory_memory.cpython-313.pyc,,
|
||||||
|
agentscope/memory/__pycache__/_long_term_memory_base.cpython-313.pyc,,
|
||||||
|
agentscope/memory/__pycache__/_mem0_long_term_memory.cpython-313.pyc,,
|
||||||
|
agentscope/memory/__pycache__/_mem0_utils.cpython-313.pyc,,
|
||||||
|
agentscope/memory/__pycache__/_memory_base.cpython-313.pyc,,
|
||||||
|
agentscope/memory/_in_memory_memory.py,sha256=wEpxEqJ0rKui4AVrPz4Bs7JYLc4FmZmp6brUxm9U9nw,3694
|
||||||
|
agentscope/memory/_long_term_memory_base.py,sha256=PQj1W7cLZVyO6gEbaqCeJm_uGG4oa6kRb_3PNgtwbBA,2976
|
||||||
|
agentscope/memory/_mem0_long_term_memory.py,sha256=UsETvCSdwdaP59CphU5SB_BmOvCUHVkmDdFd0POFScs,21243
|
||||||
|
agentscope/memory/_mem0_utils.py,sha256=9cKMy6KUKfAT5CF5oaIrJp90-qFaOZRMbLVg1VZvyMA,7617
|
||||||
|
agentscope/memory/_memory_base.py,sha256=HcM23pIsXULYX6lT1DsGODG72ZWo3EA2TE6kbJEXQG4,1218
|
||||||
|
agentscope/memory/_reme/__init__.py,sha256=mIuY8wRbVVYrQk_S05RBLivY0cnWDfq7Ere-oASf0G0,364
|
||||||
|
agentscope/memory/_reme/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/memory/_reme/__pycache__/_reme_long_term_memory_base.cpython-313.pyc,,
|
||||||
|
agentscope/memory/_reme/__pycache__/_reme_personal_long_term_memory.cpython-313.pyc,,
|
||||||
|
agentscope/memory/_reme/__pycache__/_reme_task_long_term_memory.cpython-313.pyc,,
|
||||||
|
agentscope/memory/_reme/__pycache__/_reme_tool_long_term_memory.cpython-313.pyc,,
|
||||||
|
agentscope/memory/_reme/_reme_long_term_memory_base.py,sha256=o9bvTq1Zz4OESBifog0rX-Pax1N4dkcEgbxMknLCnz4,13747
|
||||||
|
agentscope/memory/_reme/_reme_personal_long_term_memory.py,sha256=y4BpRymoNjw4ZHTYE6ThflwDgKroeHOLotgrpCNd5n8,13818
|
||||||
|
agentscope/memory/_reme/_reme_task_long_term_memory.py,sha256=QHqLJojl98JO9Gv4Q6QrJGtyUvffRAIOkY0w0zNkcVo,14324
|
||||||
|
agentscope/memory/_reme/_reme_tool_long_term_memory.py,sha256=DyKOJQYKm5eAhiqd91Xb7bBl23RO6jf0_k98P_AuK7Q,17675
|
||||||
|
agentscope/message/__init__.py,sha256=Bxh_L9v-eHVe4_vrhXhMfaNsk5TN45i0PwuKiIju1I0,519
|
||||||
|
agentscope/message/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/message/__pycache__/_message_base.cpython-313.pyc,,
|
||||||
|
agentscope/message/__pycache__/_message_block.cpython-313.pyc,,
|
||||||
|
agentscope/message/_message_base.py,sha256=sYw8MiuErVEcqaS6mPAoqTwsrJAyb9UqP-hZyqltNYY,6895
|
||||||
|
agentscope/message/_message_block.py,sha256=oJQsnqm2exxIZjUn3j6CV77rlwL-QIbSf15PCGiTOgg,2651
|
||||||
|
agentscope/model/__init__.py,sha256=BrUp6U4Tn7GwuaKvJ4rC3GIwrNXZe9jxBYxftJV7OZA,603
|
||||||
|
agentscope/model/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/model/__pycache__/_anthropic_model.cpython-313.pyc,,
|
||||||
|
agentscope/model/__pycache__/_dashscope_model.cpython-313.pyc,,
|
||||||
|
agentscope/model/__pycache__/_gemini_model.cpython-313.pyc,,
|
||||||
|
agentscope/model/__pycache__/_model_base.cpython-313.pyc,,
|
||||||
|
agentscope/model/__pycache__/_model_response.cpython-313.pyc,,
|
||||||
|
agentscope/model/__pycache__/_model_usage.cpython-313.pyc,,
|
||||||
|
agentscope/model/__pycache__/_ollama_model.cpython-313.pyc,,
|
||||||
|
agentscope/model/__pycache__/_openai_model.cpython-313.pyc,,
|
||||||
|
agentscope/model/__pycache__/_trinity_model.cpython-313.pyc,,
|
||||||
|
agentscope/model/_anthropic_model.py,sha256=DPPI6WF1MD6ApSpu-tFXjM620GE8X3NqF4fpfuquA6U,18902
|
||||||
|
agentscope/model/_dashscope_model.py,sha256=dCY6Z8xYyyMU2Y3HBUHaNaesBBCUW1FDBy_11VIm7dE,19329
|
||||||
|
agentscope/model/_gemini_model.py,sha256=KUCZZelXec7ZXAo_XM8nyjrus7ATY2OYxXRPX0Uze5Y,17453
|
||||||
|
agentscope/model/_model_base.py,sha256=9lRp9WT-NyOsoHLw2pKvr-aBOqsoFp_PLziXz9Jvsho,2039
|
||||||
|
agentscope/model/_model_response.py,sha256=aER8B2-rg_Uu0MRSxknTIO_J0Q5yzyvH1aKfNdaW35k,1295
|
||||||
|
agentscope/model/_model_usage.py,sha256=o9OvCM4CwYqqfa_w2oz80poIU8a2x9tf4jrkynr0g3o,560
|
||||||
|
agentscope/model/_ollama_model.py,sha256=1l2fSIs2AakdZCV6rFVHANpokpZ0ZjAUFheY4sy6Eq0,12319
|
||||||
|
agentscope/model/_openai_model.py,sha256=DA3LspB1X_Z-z9HNsZK_-df6oJjwvUNso1GzpgiRwhw,20201
|
||||||
|
agentscope/model/_trinity_model.py,sha256=aq0pCGcDZfd3OX2bqM0RLhcsD2Dguom2QrwcB3CRjMw,2362
|
||||||
|
agentscope/module/__init__.py,sha256=zhS2IA0PzSZalXiyu1jUXnz32ESQNn7RJKJJuSInE5o,130
|
||||||
|
agentscope/module/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/module/__pycache__/_state_module.cpython-313.pyc,,
|
||||||
|
agentscope/module/_state_module.py,sha256=nI1cm1Sa2ycFiFxs_tpo9aOnxWSqXGr8gVCTlRw5bVI,5644
|
||||||
|
agentscope/pipeline/__init__.py,sha256=KwzeVx8Ugl0Ljk9B_ziRrR-5YfBHtDW5fjdM3CVJmgc,496
|
||||||
|
agentscope/pipeline/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/pipeline/__pycache__/_class.cpython-313.pyc,,
|
||||||
|
agentscope/pipeline/__pycache__/_functional.cpython-313.pyc,,
|
||||||
|
agentscope/pipeline/__pycache__/_msghub.cpython-313.pyc,,
|
||||||
|
agentscope/pipeline/_class.py,sha256=NQS56cj3eOK2j5F28-V3Sdh1cMaOT3N68y10hqH24MY,2594
|
||||||
|
agentscope/pipeline/_functional.py,sha256=lVtm7UijSV7qyJ9C-aR_4dpmTON88nxZ8oUKxpvOoyU,5903
|
||||||
|
agentscope/pipeline/_msghub.py,sha256=P2QqwvwJeiSE82eI_NDszeKHRHV4bOseisTnLwVUT34,5227
|
||||||
|
agentscope/plan/__init__.py,sha256=TZ48k6DSZh5t9piWMl90R-gCBnY0ipLe875ZMfDtKts,418
|
||||||
|
agentscope/plan/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/plan/__pycache__/_in_memory_storage.cpython-313.pyc,,
|
||||||
|
agentscope/plan/__pycache__/_plan_model.cpython-313.pyc,,
|
||||||
|
agentscope/plan/__pycache__/_plan_notebook.cpython-313.pyc,,
|
||||||
|
agentscope/plan/__pycache__/_storage_base.cpython-313.pyc,,
|
||||||
|
agentscope/plan/_in_memory_storage.py,sha256=I32B4nJCu7BYyb1Tgm4QLRI_K0Q27kMNRt96N-y0wLM,2094
|
||||||
|
agentscope/plan/_plan_model.py,sha256=yb4KK_JL1C3QVoX4gCeiSghHFaE39ijq4lxbGm_viZI,6418
|
||||||
|
agentscope/plan/_plan_notebook.py,sha256=LmZOJxQj862Bp4TR_gZU_lqF14uhPHVnENjVLVIOVas,32621
|
||||||
|
agentscope/plan/_storage_base.py,sha256=M1BFqDIiHthFJQFi3gDZNaO12S9FZ_snTFMbqtTipIw,725
|
||||||
|
agentscope/rag/__init__.py,sha256=ZCXDDkeHqYGyl-HuPpWzVj_5mP24fUXSDLpHRc0Hz-U,674
|
||||||
|
agentscope/rag/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/rag/__pycache__/_document.cpython-313.pyc,,
|
||||||
|
agentscope/rag/__pycache__/_knowledge_base.cpython-313.pyc,,
|
||||||
|
agentscope/rag/__pycache__/_simple_knowledge.cpython-313.pyc,,
|
||||||
|
agentscope/rag/_document.py,sha256=9UvKeKu0WBz2nwNj3cgvx6fouVzBa7LJFcDvpVXxw8A,1199
|
||||||
|
agentscope/rag/_knowledge_base.py,sha256=pswLgq1VPKD9UiHns1I_dlI0m6A-9GNyAI67Pm_aJ2A,4441
|
||||||
|
agentscope/rag/_reader/__init__.py,sha256=IvJ2FAphm5CgWoaI1CEPmJPArveQtZv5-Shfkf3yitg,413
|
||||||
|
agentscope/rag/_reader/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/rag/_reader/__pycache__/_image_reader.cpython-313.pyc,,
|
||||||
|
agentscope/rag/_reader/__pycache__/_pdf_reader.cpython-313.pyc,,
|
||||||
|
agentscope/rag/_reader/__pycache__/_reader_base.cpython-313.pyc,,
|
||||||
|
agentscope/rag/_reader/__pycache__/_text_reader.cpython-313.pyc,,
|
||||||
|
agentscope/rag/_reader/__pycache__/_word_reader.cpython-313.pyc,,
|
||||||
|
agentscope/rag/_reader/_image_reader.py,sha256=kY-jLuzaUyILsviOaO4Fzejupa7I_OJGCRVheo-D0YA,1902
|
||||||
|
agentscope/rag/_reader/_pdf_reader.py,sha256=auptyMvJ6CFDOHqF-Z8s6gHoYtA5mldnQsz3k7AfSv8,2758
|
||||||
|
agentscope/rag/_reader/_reader_base.py,sha256=qY5xkACT1H28lWLjUUFB-Dn_xqpUexPwJu-pMViTmVI,900
|
||||||
|
agentscope/rag/_reader/_text_reader.py,sha256=Loq0fFMhj53mhZPcezG_hxIx3LQSD5FaTsxQmXDqZos,5046
|
||||||
|
agentscope/rag/_reader/_word_reader.py,sha256=0eh_7d_Fa5_6g5xsMq7koSWz4Uv6y3fCPY3LoZ958TI,17385
|
||||||
|
agentscope/rag/_simple_knowledge.py,sha256=rMqMEy6QInrjUOX85g8NRlG7LHSrip4hkYVcv17lOMA,2580
|
||||||
|
agentscope/rag/_store/__init__.py,sha256=u7qAWDjacgKxfzIPD7LeDXb9pbvWX6WjgDrdvMyQzJM,305
|
||||||
|
agentscope/rag/_store/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/rag/_store/__pycache__/_milvuslite_store.cpython-313.pyc,,
|
||||||
|
agentscope/rag/_store/__pycache__/_qdrant_store.cpython-313.pyc,,
|
||||||
|
agentscope/rag/_store/__pycache__/_store_base.cpython-313.pyc,,
|
||||||
|
agentscope/rag/_store/_milvuslite_store.py,sha256=1FUs6zlVoOjwz3YLc-oFqyXqSQjEdKD_uNhCDjURqHI,9059
|
||||||
|
agentscope/rag/_store/_qdrant_store.py,sha256=04Xsfr9WM7g7MNRyPO1hEd_-hbIqPkvexYKMY9ukR80,6250
|
||||||
|
agentscope/rag/_store/_store_base.py,sha256=RaXNmcGu6kZMJjM7pfSjVWcv4iCxMt9LMbJpSMrI0ZM,1658
|
||||||
|
agentscope/session/__init__.py,sha256=GY2Skir_T7y19pnywNVBZ3H3ePqsnShva-ljgeiCLiQ,196
|
||||||
|
agentscope/session/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/session/__pycache__/_json_session.cpython-313.pyc,,
|
||||||
|
agentscope/session/__pycache__/_session_base.cpython-313.pyc,,
|
||||||
|
agentscope/session/_json_session.py,sha256=4NvJPK4qplKdASSHeu7BugpCSxO8BNHRbSNPzna6g-s,3820
|
||||||
|
agentscope/session/_session_base.py,sha256=pKbEHnOHJuxaccGobPo2z984W3OrZS3Rl9oCwrpibMY,859
|
||||||
|
agentscope/token/__init__.py,sha256=0PyydmrOKp-Al3CUqM_1WiCSZc9hSR3XEXV7mXCv48k,487
|
||||||
|
agentscope/token/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/token/__pycache__/_anthropic_token_counter.cpython-313.pyc,,
|
||||||
|
agentscope/token/__pycache__/_gemini_token_counter.cpython-313.pyc,,
|
||||||
|
agentscope/token/__pycache__/_huggingface_token_counter.cpython-313.pyc,,
|
||||||
|
agentscope/token/__pycache__/_openai_token_counter.cpython-313.pyc,,
|
||||||
|
agentscope/token/__pycache__/_token_base.cpython-313.pyc,,
|
||||||
|
agentscope/token/_anthropic_token_counter.py,sha256=-1c-OrsfaNvSCEHzgRNSFL7HUQCAuoe9q3nNEk4J9YE,1853
|
||||||
|
agentscope/token/_gemini_token_counter.py,sha256=vcWHo8iHR1JLhDTM3pS6-mpxEUWCKceVll_ypq7mKhU,1385
|
||||||
|
agentscope/token/_huggingface_token_counter.py,sha256=ksxFpqPde16VptiSV8RmHasgld1bp_fz16j6t4baWtk,2846
|
||||||
|
agentscope/token/_openai_token_counter.py,sha256=Aq2YCnCOvPUOfr4r5OeQqqom_VRAPyUdckqIbx3giiQ,11365
|
||||||
|
agentscope/token/_token_base.py,sha256=ukxfNg8nBL_MLd9rzkpy5aP8s-uqEKbeZGtAYZyCBzA,388
|
||||||
|
agentscope/tool/__init__.py,sha256=lXMHKD91iPK2rSY53cDg39kMXwQsedG-rf39gFRd0Go,1020
|
||||||
|
agentscope/tool/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/tool/__pycache__/_async_wrapper.cpython-313.pyc,,
|
||||||
|
agentscope/tool/__pycache__/_registered_tool_function.cpython-313.pyc,,
|
||||||
|
agentscope/tool/__pycache__/_response.cpython-313.pyc,,
|
||||||
|
agentscope/tool/__pycache__/_toolkit.cpython-313.pyc,,
|
||||||
|
agentscope/tool/_async_wrapper.py,sha256=tfv6edszdHC03IsL0Z3EvVAII0zEdYavD3wBI__L-zM,3474
|
||||||
|
agentscope/tool/_coding/__init__.py,sha256=e_XNdZZo6-gS7EvY_AEM2h0q3XtYm1voCKbojypfwn4,232
|
||||||
|
agentscope/tool/_coding/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/tool/_coding/__pycache__/_python.cpython-313.pyc,,
|
||||||
|
agentscope/tool/_coding/__pycache__/_shell.cpython-313.pyc,,
|
||||||
|
agentscope/tool/_coding/_python.py,sha256=FwnidjGPYBRsLGX0FPICjlApISg7eL4GZoVBvFrObik,2714
|
||||||
|
agentscope/tool/_coding/_shell.py,sha256=pgivhc2CSs8mBWSh5ax9NgWI07q_b7WoKV7hdI8h448,2266
|
||||||
|
agentscope/tool/_multi_modality/__init__.py,sha256=H9VpqWCV0V7WXvqqYWjbpwmafJHtq_pDm-RV7P3vG0s,678
|
||||||
|
agentscope/tool/_multi_modality/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/tool/_multi_modality/__pycache__/_dashscope_tools.cpython-313.pyc,,
|
||||||
|
agentscope/tool/_multi_modality/__pycache__/_openai_tools.cpython-313.pyc,,
|
||||||
|
agentscope/tool/_multi_modality/_dashscope_tools.py,sha256=6piJ5fjVaQTYaq4w3UQXGS0ANY29lkCqyc3_keomlbY,8908
|
||||||
|
agentscope/tool/_multi_modality/_openai_tools.py,sha256=UaZOHHAWEi7Y_uG_n23Q8OTh45Cb-hD4t5-oR6o4K_Q,20873
|
||||||
|
agentscope/tool/_registered_tool_function.py,sha256=jI8VMSeWlj-EGq7IKlydBQcC2jjOZEDQTdnkxEm7jng,3449
|
||||||
|
agentscope/tool/_response.py,sha256=3WdPFj1uRdxvkrECfMlUjzPVg-PseeSVNF5SWn1dCOs,938
|
||||||
|
agentscope/tool/_text_file/__init__.py,sha256=tnQP6RCS1wHff65ycZkXE4Fxcw5OXZe50_6dD5hfa9o,276
|
||||||
|
agentscope/tool/_text_file/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/tool/_text_file/__pycache__/_utils.cpython-313.pyc,,
|
||||||
|
agentscope/tool/_text_file/__pycache__/_view_text_file.cpython-313.pyc,,
|
||||||
|
agentscope/tool/_text_file/__pycache__/_write_text_file.cpython-313.pyc,,
|
||||||
|
agentscope/tool/_text_file/_utils.py,sha256=vizy7C6rNmp8z5aiNbrrsMdoeX8bws4FnnyEixlo0jU,2645
|
||||||
|
agentscope/tool/_text_file/_view_text_file.py,sha256=MHq0eiNiBMSDY7Dne8Ch6-YqIS0i8alP3dX2gzxi-nA,2280
|
||||||
|
agentscope/tool/_text_file/_write_text_file.py,sha256=zZcafaRg53o2DT1Q1t9en4Z8sRxaRwREAbsfdLfSpOI,7333
|
||||||
|
agentscope/tool/_toolkit.py,sha256=2iSCpI9zRmNgD_g_PVE-N8K060_DU4S9NDzQ7tvnW30,34654
|
||||||
|
agentscope/tracing/__init__.py,sha256=R7Xegg62pD5UDey0w4ZyAJ6LVt6_qSkjlrOIoV3mHpk,382
|
||||||
|
agentscope/tracing/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/tracing/__pycache__/_attributes.cpython-313.pyc,,
|
||||||
|
agentscope/tracing/__pycache__/_setup.cpython-313.pyc,,
|
||||||
|
agentscope/tracing/__pycache__/_trace.cpython-313.pyc,,
|
||||||
|
agentscope/tracing/__pycache__/_types.cpython-313.pyc,,
|
||||||
|
agentscope/tracing/_attributes.py,sha256=-R_z4rI17u4pOyK9RWtWPmKX0uEL1YmffcJhAEJanvM,1774
|
||||||
|
agentscope/tracing/_setup.py,sha256=QwhHf_NyR-4lBJChNa616pze7CV7HPFq0mNoWxUnCQg,874
|
||||||
|
agentscope/tracing/_trace.py,sha256=Iclrx0Uxpjq4OwrGfCvl_TADn7eSm85R2ZhJiwYdG-w,21677
|
||||||
|
agentscope/tracing/_types.py,sha256=EE-Y6btCihsNuC27XiLlTdqLy1C4CCoENthPDerw8mU,528
|
||||||
|
agentscope/tune/__init__.py,sha256=VpKbNhHLr6GAQ4zJJ-hEy9IOCqLB7YQTNt3DKQKbq0g,195
|
||||||
|
agentscope/tune/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/tune/__pycache__/_tune.cpython-313.pyc,,
|
||||||
|
agentscope/tune/__pycache__/_workflow.cpython-313.pyc,,
|
||||||
|
agentscope/tune/_tune.py,sha256=elVcwVuRRlPghcaYQZAYhiwchV4VqIL-PDb86HaV_ws,2520
|
||||||
|
agentscope/tune/_workflow.py,sha256=TLrri2aRfVamTzDK8FaeHUDDSZZnfhos8JvseeFoHiU,2125
|
||||||
|
agentscope/types/__init__.py,sha256=_AX5wH9fPHkNEwCOQb2ycdh62J6IRXqYYcplJOcWjFQ,408
|
||||||
|
agentscope/types/__pycache__/__init__.cpython-313.pyc,,
|
||||||
|
agentscope/types/__pycache__/_hook.cpython-313.pyc,,
|
||||||
|
agentscope/types/__pycache__/_json.cpython-313.pyc,,
|
||||||
|
agentscope/types/__pycache__/_object.cpython-313.pyc,,
|
||||||
|
agentscope/types/__pycache__/_tool.cpython-313.pyc,,
|
||||||
|
agentscope/types/_hook.py,sha256=st--2oBVuLwNaNk5DQXkYA7xp8X4HaqTaXf2lVDAPM0,427
|
||||||
|
agentscope/types/_json.py,sha256=MXTFO_TufiyRy0X_OuEfuoPqWLRUYRRwurKu-o-m5oI,308
|
||||||
|
agentscope/types/_object.py,sha256=dHR84mJvBFesbYj46mQO9ssw38dPuKpJaCbySprLWZE,111
|
||||||
|
agentscope/types/_tool.py,sha256=J22DtLNDoMTZp_cOkGfrjpmG-qFPWgqaKhcXsyomC58,845
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Wheel-Version: 1.0
|
||||||
|
Generator: setuptools (80.9.0)
|
||||||
|
Root-Is-Purelib: true
|
||||||
|
Tag: py3-none-any
|
||||||
|
|
||||||
@@ -0,0 +1,391 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2024 Alibaba
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
Some codes of tests/run.py is modified from
|
||||||
|
https://github.com/alibaba/FederatedScope/blob/master/tests/run.py, which is
|
||||||
|
also licensed under the terms of the Apache 2.0.
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Code in src/agentscope/web/static/js/socket.io.js is adapted from
|
||||||
|
https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.1.3/socket.io.js (MIT License)
|
||||||
|
|
||||||
|
Copyright (c) 2014-2021 Guillermo Rauch
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Code in src/agentscope/web/static/js/jquery-3.3.1.min.js is adapted from
|
||||||
|
https://code.jquery.com/jquery-3.3.1.min.js (MIT License)
|
||||||
|
|
||||||
|
Copyright (c) JS Foundation and other contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Code in src/agentscope/web/static/js/bootstrap.bundle.min.js is adapted from
|
||||||
|
https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/js/bootstrap.bundle.min.js
|
||||||
|
(MIT License)
|
||||||
|
|
||||||
|
Copyright (c) 2011-2019 The Bootstrap Authors (https://github
|
||||||
|
.com/twbs/bootstrap/graphs/contributors)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Code in src/agentscope/web/static/js/bootstrap-table.min.js is adapted from
|
||||||
|
https://unpkg.com/bootstrap-table@1.18.0/dist/bootstrap-table.min.js (MIT
|
||||||
|
License)
|
||||||
|
|
||||||
|
Copyright (c) wenzhixin <wenzhixin2010@gmail.com> (http://wenzhixin.net.cn/)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Code in src/agentscope/web/static/css/bootstrap.min.css is adapted from
|
||||||
|
https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/css/bootstrap.min.css (MIT
|
||||||
|
License)
|
||||||
|
|
||||||
|
Copyright 2011-2019 The Bootstrap Authors
|
||||||
|
Copyright 2011-2019 Twitter, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Fonts in src/agentscope/web/static/fonts/KRYPTON.ttf is adapted from
|
||||||
|
https://github.com/githubnext/monaspace (SIL Open Font License 1.1). These
|
||||||
|
fonts are distributed with their original license. See https://github
|
||||||
|
.com/githubnext/monaspace/blob/main/LICENSE for the full text of the license.
|
||||||
|
The following font families are included:
|
||||||
|
|
||||||
|
- Monaspace (with subfamilies: Krypton)
|
||||||
|
|
||||||
|
Copyright (c) 2023, GitHub https://github.com/githubnext/monaspace
|
||||||
|
with Reserved Font Name "Monaspace", including subfamilies: "Argon", "Neon",
|
||||||
|
"Xenon", "Radon", and "Krypton"
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Fonts in src/agentscope/web/static/fonts/OSWALD.ttf is adapted from
|
||||||
|
https://fonts.google.com/specimen/Oswald (SIL Open Font License 1.1). These
|
||||||
|
fonts are distributed with their original license. See https://github
|
||||||
|
.com/googlefonts/OswaldFont/blob/main/OFL.txt for the full text of the license.
|
||||||
|
|
||||||
|
Copyright 2016 The Oswald Project Authors (https://github
|
||||||
|
.com/googlefonts/OswaldFont)
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
agentscope
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""The agentscope serialization module"""
|
||||||
|
import os
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from . import exception
|
||||||
|
from . import module
|
||||||
|
from . import message
|
||||||
|
from . import model
|
||||||
|
from . import tool
|
||||||
|
from . import formatter
|
||||||
|
from . import memory
|
||||||
|
from . import agent
|
||||||
|
from . import session
|
||||||
|
from . import embedding
|
||||||
|
from . import token
|
||||||
|
from . import evaluate
|
||||||
|
from . import pipeline
|
||||||
|
from . import tracing
|
||||||
|
from . import rag
|
||||||
|
|
||||||
|
from ._logging import (
|
||||||
|
logger,
|
||||||
|
setup_logger,
|
||||||
|
)
|
||||||
|
from .hooks import _equip_as_studio_hooks
|
||||||
|
from ._version import __version__
|
||||||
|
|
||||||
|
|
||||||
|
def init(
|
||||||
|
project: str | None = None,
|
||||||
|
name: str | None = None,
|
||||||
|
logging_path: str | None = None,
|
||||||
|
logging_level: str = "INFO",
|
||||||
|
studio_url: str | None = None,
|
||||||
|
tracing_url: str | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the agentscope library.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project (`str | None`, optional):
|
||||||
|
The project name.
|
||||||
|
name (`str | None`, optional):
|
||||||
|
The name of the run.
|
||||||
|
logging_path (`str | None`, optional):
|
||||||
|
The path to saving the log file. If not provided, logs will not be
|
||||||
|
saved.
|
||||||
|
logging_level (`str | None`, optional):
|
||||||
|
The logging level. Defaults to "INFO".
|
||||||
|
studio_url (`str | None`, optional):
|
||||||
|
The URL of the AgentScope Studio to connect to.
|
||||||
|
tracing_url (`str | None`, optional):
|
||||||
|
The URL of the tracing endpoint, which can connect to third-party
|
||||||
|
OpenTelemetry tracing platforms like Arize-Phoenix and Langfuse.
|
||||||
|
If not provided and `studio_url` is provided, it will send traces
|
||||||
|
to the AgentScope Studio's tracing endpoint.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from . import _config
|
||||||
|
|
||||||
|
if project:
|
||||||
|
_config.project = project
|
||||||
|
|
||||||
|
if name:
|
||||||
|
_config.name = name
|
||||||
|
|
||||||
|
setup_logger(logging_level, logging_path)
|
||||||
|
|
||||||
|
if studio_url:
|
||||||
|
# Register the run
|
||||||
|
data = {
|
||||||
|
"id": _config.run_id,
|
||||||
|
"project": _config.project,
|
||||||
|
"name": _config.name,
|
||||||
|
"timestamp": _config.created_at,
|
||||||
|
"pid": os.getpid(),
|
||||||
|
"status": "running",
|
||||||
|
# Deprecated fields
|
||||||
|
"run_dir": "",
|
||||||
|
}
|
||||||
|
response = requests.post(
|
||||||
|
url=f"{studio_url}/trpc/registerRun",
|
||||||
|
json=data,
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
from .agent import UserAgent, StudioUserInput
|
||||||
|
|
||||||
|
UserAgent.override_class_input_method(
|
||||||
|
StudioUserInput(
|
||||||
|
studio_url=studio_url,
|
||||||
|
run_id=_config.run_id,
|
||||||
|
max_retries=3,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
_equip_as_studio_hooks(studio_url)
|
||||||
|
|
||||||
|
if tracing_url:
|
||||||
|
endpoint = tracing_url
|
||||||
|
else:
|
||||||
|
endpoint = studio_url.strip("/") + "/v1/traces" if studio_url else None
|
||||||
|
|
||||||
|
if endpoint:
|
||||||
|
from .tracing import setup_tracing
|
||||||
|
|
||||||
|
setup_tracing(endpoint=endpoint)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
# modules
|
||||||
|
"exception",
|
||||||
|
"module",
|
||||||
|
"message",
|
||||||
|
"model",
|
||||||
|
"tool",
|
||||||
|
"formatter",
|
||||||
|
"memory",
|
||||||
|
"agent",
|
||||||
|
"session",
|
||||||
|
"logger",
|
||||||
|
"embedding",
|
||||||
|
"token",
|
||||||
|
"evaluate",
|
||||||
|
"pipeline",
|
||||||
|
"tracing",
|
||||||
|
"rag",
|
||||||
|
# functions
|
||||||
|
"init",
|
||||||
|
"setup_logger",
|
||||||
|
"__version__",
|
||||||
|
]
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,23 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""The runtime configuration in agentscope.
|
||||||
|
|
||||||
|
.. note:: You should import this module as ``import ._config``, then use the
|
||||||
|
variables defined in this module, instead of ``from ._config import xxx``.
|
||||||
|
Because when the variables are changed, the changes will not be reflected in
|
||||||
|
the imported module.
|
||||||
|
"""
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import shortuuid
|
||||||
|
|
||||||
|
|
||||||
|
def _generate_random_suffix(length: int) -> str:
|
||||||
|
"""Generate a random suffix."""
|
||||||
|
return shortuuid.uuid()[:length]
|
||||||
|
|
||||||
|
|
||||||
|
project = "UnnamedProject_At" + datetime.now().strftime("%Y%m%d")
|
||||||
|
name = datetime.now().strftime("%H%M%S_") + _generate_random_suffix(4)
|
||||||
|
run_id: str = shortuuid.uuid()
|
||||||
|
created_at: str = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
||||||
|
trace_enabled: bool = False
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""The logger for agentscope."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
_DEFAULT_FORMAT = (
|
||||||
|
"%(asctime)s | %(levelname)-7s | "
|
||||||
|
"%(module)s:%(funcName)s:%(lineno)s - %(message)s"
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger("as")
|
||||||
|
|
||||||
|
|
||||||
|
def setup_logger(
|
||||||
|
level: str,
|
||||||
|
filepath: str | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Set up the agentscope logger.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
level (`str`):
|
||||||
|
The logging level, chosen from "INFO", "DEBUG", "WARNING",
|
||||||
|
"ERROR", "CRITICAL".
|
||||||
|
filepath (`str | None`, optional):
|
||||||
|
The filepath to save the logging output.
|
||||||
|
"""
|
||||||
|
if level not in ["INFO", "DEBUG", "WARNING", "ERROR", "CRITICAL"]:
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid logging level: {level}. Must be one of "
|
||||||
|
f"'INFO', 'DEBUG', 'WARNING', 'ERROR', 'CRITICAL'.",
|
||||||
|
)
|
||||||
|
logger.handlers.clear()
|
||||||
|
logger.setLevel(level)
|
||||||
|
handler = logging.StreamHandler()
|
||||||
|
handler.setFormatter(logging.Formatter(_DEFAULT_FORMAT))
|
||||||
|
logger.addHandler(handler)
|
||||||
|
|
||||||
|
if filepath:
|
||||||
|
handler = logging.FileHandler(filepath)
|
||||||
|
handler.setFormatter(logging.Formatter(_DEFAULT_FORMAT))
|
||||||
|
logger.addHandler(handler)
|
||||||
|
|
||||||
|
logger.propagate = False
|
||||||
|
|
||||||
|
|
||||||
|
setup_logger("INFO")
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,285 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""The common utilities for agentscope library."""
|
||||||
|
import asyncio
|
||||||
|
import base64
|
||||||
|
import functools
|
||||||
|
import inspect
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import types
|
||||||
|
import typing
|
||||||
|
import uuid
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Union, Any, Callable, Type, Dict
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from json_repair import repair_json
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from .._logging import logger
|
||||||
|
|
||||||
|
if typing.TYPE_CHECKING:
|
||||||
|
from mcp.types import Tool
|
||||||
|
else:
|
||||||
|
Tool = "mcp.types.Tool"
|
||||||
|
|
||||||
|
|
||||||
|
def _json_loads_with_repair(
|
||||||
|
json_str: str,
|
||||||
|
) -> Union[dict, list, str, float, int, bool, None]:
|
||||||
|
"""The given json_str maybe incomplete, e.g. '{"key', so we need to
|
||||||
|
repair and load it into a Python object.
|
||||||
|
"""
|
||||||
|
repaired = json_str
|
||||||
|
try:
|
||||||
|
repaired = repair_json(json_str)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
return json.loads(repaired)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
raise ValueError(
|
||||||
|
f"Failed to decode JSON string `{json_str}` after repairing it "
|
||||||
|
f"into `{repaired}`. Error: {e}",
|
||||||
|
) from e
|
||||||
|
|
||||||
|
|
||||||
|
def _is_accessible_local_file(url: str) -> bool:
|
||||||
|
"""Check if the given URL is a local URL."""
|
||||||
|
return os.path.isfile(url)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_timestamp(add_random_suffix: bool = False) -> str:
|
||||||
|
"""Get the current timestamp in the format YYYY-MM-DD HH:MM:SS.sss."""
|
||||||
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
||||||
|
|
||||||
|
if add_random_suffix:
|
||||||
|
# Add a random suffix to the timestamp
|
||||||
|
timestamp += f"_{os.urandom(3).hex()}"
|
||||||
|
|
||||||
|
return timestamp
|
||||||
|
|
||||||
|
|
||||||
|
async def _is_async_func(func: Callable) -> bool:
|
||||||
|
"""Check if the given function is an async function, including
|
||||||
|
coroutine functions, async generators, and coroutine objects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return (
|
||||||
|
inspect.iscoroutinefunction(func)
|
||||||
|
or inspect.isasyncgenfunction(func)
|
||||||
|
or isinstance(func, types.CoroutineType)
|
||||||
|
or isinstance(func, types.GeneratorType)
|
||||||
|
and asyncio.iscoroutine(func)
|
||||||
|
or isinstance(func, functools.partial)
|
||||||
|
and await _is_async_func(func.func)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _execute_async_or_sync_func(
|
||||||
|
func: Callable,
|
||||||
|
*args: Any,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> Any:
|
||||||
|
"""Execute an async or sync function based on its type.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
func (`Callable`):
|
||||||
|
The function to be executed, which can be either async or sync.
|
||||||
|
*args (`Any`):
|
||||||
|
Positional arguments to be passed to the function.
|
||||||
|
**kwargs (`Any`):
|
||||||
|
Keyword arguments to be passed to the function.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
`Any`:
|
||||||
|
The result of the function execution.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if await _is_async_func(func):
|
||||||
|
return await func(*args, **kwargs)
|
||||||
|
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_bytes_from_web_url(
|
||||||
|
url: str,
|
||||||
|
max_retries: int = 3,
|
||||||
|
) -> str:
|
||||||
|
"""Get the bytes from a given URL.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
url (`str`):
|
||||||
|
The URL to fetch the bytes from.
|
||||||
|
max_retries (`int`, defaults to `3`):
|
||||||
|
The maximum number of retries.
|
||||||
|
"""
|
||||||
|
for _ in range(max_retries):
|
||||||
|
try:
|
||||||
|
response = requests.get(url)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.content.decode("utf-8")
|
||||||
|
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
return base64.b64encode(response.content).decode("ascii")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(
|
||||||
|
"Failed to fetch bytes from URL %s. Error %s. Retrying...",
|
||||||
|
url,
|
||||||
|
str(e),
|
||||||
|
)
|
||||||
|
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Failed to fetch bytes from URL `{url}` after {max_retries} retries.",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _save_base64_data(
|
||||||
|
media_type: str,
|
||||||
|
base64_data: str,
|
||||||
|
) -> str:
|
||||||
|
"""Save the base64 data to a temp file and return the file path. The
|
||||||
|
extension is guessed from the MIME type.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
media_type (`str`):
|
||||||
|
The MIME type of the data, e.g. "image/png", "audio/mpeg".
|
||||||
|
base64_data (`str):
|
||||||
|
The base64 data to be saved.
|
||||||
|
"""
|
||||||
|
extension = "." + media_type.split("/")[-1]
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
suffix=f".{extension}",
|
||||||
|
delete=False,
|
||||||
|
) as temp_file:
|
||||||
|
decoded_data = base64.b64decode(base64_data)
|
||||||
|
temp_file.write(decoded_data)
|
||||||
|
temp_file.close()
|
||||||
|
return temp_file.name
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_json_schema_from_mcp_tool(tool: Tool) -> dict[str, Any]:
|
||||||
|
"""Extract JSON schema from MCP tool."""
|
||||||
|
|
||||||
|
return {
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": tool.name,
|
||||||
|
"description": tool.description,
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": tool.inputSchema.get(
|
||||||
|
"properties",
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
"required": tool.inputSchema.get(
|
||||||
|
"required",
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _remove_title_field(schema: dict) -> None:
|
||||||
|
"""Remove the title field from the JSON schema to avoid
|
||||||
|
misleading the LLM."""
|
||||||
|
# The top level title field
|
||||||
|
if "title" in schema:
|
||||||
|
schema.pop("title")
|
||||||
|
|
||||||
|
# properties
|
||||||
|
if "properties" in schema:
|
||||||
|
for prop in schema["properties"].values():
|
||||||
|
if isinstance(prop, dict):
|
||||||
|
_remove_title_field(prop)
|
||||||
|
|
||||||
|
# items
|
||||||
|
if "items" in schema and isinstance(schema["items"], dict):
|
||||||
|
_remove_title_field(schema["items"])
|
||||||
|
|
||||||
|
# additionalProperties
|
||||||
|
if "additionalProperties" in schema and isinstance(
|
||||||
|
schema["additionalProperties"],
|
||||||
|
dict,
|
||||||
|
):
|
||||||
|
_remove_title_field(
|
||||||
|
schema["additionalProperties"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _create_tool_from_base_model(
|
||||||
|
structured_model: Type[BaseModel],
|
||||||
|
tool_name: str = "generate_structured_output",
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Create a function tool definition from a Pydantic BaseModel.
|
||||||
|
This function converts a Pydantic BaseModel class into a tool definition
|
||||||
|
that can be used with function calling API. The resulting tool
|
||||||
|
definition includes the model's JSON schema as parameters, enabling
|
||||||
|
structured output generation by forcing the model to call this function
|
||||||
|
with properly formatted data.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
structured_model (`Type[BaseModel]`):
|
||||||
|
A Pydantic BaseModel class that defines the expected structure
|
||||||
|
for the tool's output.
|
||||||
|
tool_name (`str`, default `"generate_structured_output"`):
|
||||||
|
The tool name that used to force the LLM to generate structured
|
||||||
|
output by calling this function.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
`Dict[str, Any]`: A tool definition dictionary compatible with
|
||||||
|
function calling API, containing type ("function") and
|
||||||
|
function dictionary with name, description, and parameters
|
||||||
|
(JSON schema).
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: Example usage
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
class PersonInfo(BaseModel):
|
||||||
|
name: str
|
||||||
|
age: int
|
||||||
|
email: str
|
||||||
|
|
||||||
|
tool = _create_tool_from_base_model(PersonInfo, "extract_person")
|
||||||
|
print(tool["function"]["name"]) # extract_person
|
||||||
|
print(tool["type"]) # function
|
||||||
|
|
||||||
|
.. note:: The function automatically removes the 'title' field from
|
||||||
|
the JSON schema to ensure compatibility with function calling
|
||||||
|
format. This is handled by the internal ``_remove_title_field()``
|
||||||
|
function.
|
||||||
|
"""
|
||||||
|
schema = structured_model.model_json_schema()
|
||||||
|
|
||||||
|
_remove_title_field(schema)
|
||||||
|
tool_definition = {
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": tool_name,
|
||||||
|
"description": "Generate the required structured output with "
|
||||||
|
"this function",
|
||||||
|
"parameters": schema,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return tool_definition
|
||||||
|
|
||||||
|
|
||||||
|
def _map_text_to_uuid(text: str) -> str:
|
||||||
|
"""Map the given text to a deterministic UUID string.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text (`str`):
|
||||||
|
The input text to be mapped to a UUID.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
`str`:
|
||||||
|
A deterministic UUID string derived from the input text.
|
||||||
|
"""
|
||||||
|
return str(uuid.uuid3(uuid.NAMESPACE_DNS, text))
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""The mixin for agentscope."""
|
||||||
|
|
||||||
|
|
||||||
|
class DictMixin(dict):
|
||||||
|
"""The dictionary mixin that allows attribute-style access."""
|
||||||
|
|
||||||
|
__setattr__ = dict.__setitem__
|
||||||
|
__getattr__ = dict.__getitem__
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""The version of agentscope."""
|
||||||
|
|
||||||
|
__version__ = "1.0.7"
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""The agent base class."""
|
||||||
|
from ._agent_base import AgentBase
|
||||||
|
from ._react_agent_base import ReActAgentBase
|
||||||
|
from ._react_agent import ReActAgent
|
||||||
|
from ._user_input import (
|
||||||
|
UserInputBase,
|
||||||
|
UserInputData,
|
||||||
|
TerminalUserInput,
|
||||||
|
StudioUserInput,
|
||||||
|
)
|
||||||
|
from ._user_agent import UserAgent
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"AgentBase",
|
||||||
|
"ReActAgentBase",
|
||||||
|
"ReActAgent",
|
||||||
|
"UserInputData",
|
||||||
|
"UserInputBase",
|
||||||
|
"TerminalUserInput",
|
||||||
|
"StudioUserInput",
|
||||||
|
"UserAgent",
|
||||||
|
]
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,703 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""The agent base class in agentscope."""
|
||||||
|
import asyncio
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
from asyncio import Task, Queue
|
||||||
|
from collections import OrderedDict
|
||||||
|
from copy import deepcopy
|
||||||
|
from typing import Callable, Any
|
||||||
|
import base64
|
||||||
|
import shortuuid
|
||||||
|
import numpy as np
|
||||||
|
from typing_extensions import deprecated
|
||||||
|
|
||||||
|
from ._agent_meta import _AgentMeta
|
||||||
|
from .._logging import logger
|
||||||
|
from ..module import StateModule
|
||||||
|
from ..message import (
|
||||||
|
Msg,
|
||||||
|
AudioBlock,
|
||||||
|
ToolUseBlock,
|
||||||
|
ToolResultBlock,
|
||||||
|
ImageBlock,
|
||||||
|
VideoBlock,
|
||||||
|
)
|
||||||
|
from ..types import AgentHookTypes
|
||||||
|
|
||||||
|
|
||||||
|
class AgentBase(StateModule, metaclass=_AgentMeta):
|
||||||
|
"""Base class for asynchronous agents."""
|
||||||
|
|
||||||
|
id: str
|
||||||
|
"""The agent's unique identifier, generated using shortuuid."""
|
||||||
|
|
||||||
|
supported_hook_types: list[str] = [
|
||||||
|
"pre_reply",
|
||||||
|
"post_reply",
|
||||||
|
"pre_print",
|
||||||
|
"post_print",
|
||||||
|
"pre_observe",
|
||||||
|
"post_observe",
|
||||||
|
]
|
||||||
|
"""Supported hook types for the agent base class."""
|
||||||
|
|
||||||
|
_class_pre_reply_hooks: dict[
|
||||||
|
str,
|
||||||
|
Callable[
|
||||||
|
[
|
||||||
|
"AgentBase", # self
|
||||||
|
dict[str, Any], # kwargs
|
||||||
|
],
|
||||||
|
dict[str, Any] | None, # The modified kwargs or None
|
||||||
|
],
|
||||||
|
] = OrderedDict()
|
||||||
|
"""The class-level hook functions that will be called before the reply
|
||||||
|
function, taking `self` object, the input arguments as input, and
|
||||||
|
generating the modified arguments (if needed). Then input arguments of the
|
||||||
|
reply function will be re-organized into a keyword arguments dictionary.
|
||||||
|
If the one hook returns a new dictionary, the modified arguments will be
|
||||||
|
passed to the next hook or the original reply function."""
|
||||||
|
|
||||||
|
_class_post_reply_hooks: dict[
|
||||||
|
str,
|
||||||
|
Callable[
|
||||||
|
[
|
||||||
|
"AgentBase", # self
|
||||||
|
dict[str, Any], # kwargs
|
||||||
|
Msg, # output, the output message
|
||||||
|
],
|
||||||
|
Msg | None,
|
||||||
|
],
|
||||||
|
] = OrderedDict()
|
||||||
|
"""The class-level hook functions that will be called after the reply
|
||||||
|
function, which takes the `self` object and deep copied
|
||||||
|
positional and keyword arguments (args and kwargs), and the output message
|
||||||
|
as input. If the hook returns a message, the new message will be passed
|
||||||
|
to the next hook or the original reply function. Otherwise, the original
|
||||||
|
output will be passed instead."""
|
||||||
|
|
||||||
|
_class_pre_print_hooks: dict[
|
||||||
|
str,
|
||||||
|
Callable[
|
||||||
|
[
|
||||||
|
"AgentBase", # self
|
||||||
|
dict[str, Any], # kwargs
|
||||||
|
],
|
||||||
|
dict[str, Any] | None, # The modified kwargs or None
|
||||||
|
],
|
||||||
|
] = OrderedDict()
|
||||||
|
"""The class-level hook functions that will be called before printing,
|
||||||
|
which takes the `self` object, a deep copied arguments dictionary as input,
|
||||||
|
and output the modified arguments (if needed). """
|
||||||
|
|
||||||
|
_class_post_print_hooks: dict[
|
||||||
|
str,
|
||||||
|
Callable[
|
||||||
|
[
|
||||||
|
"AgentBase", # self
|
||||||
|
dict[str, Any], # kwargs
|
||||||
|
Any, # output, `None` if no output
|
||||||
|
],
|
||||||
|
Any,
|
||||||
|
],
|
||||||
|
] = OrderedDict()
|
||||||
|
"""The class-level hook functions that will be called after the speak
|
||||||
|
function, which takes the `self` object as input."""
|
||||||
|
|
||||||
|
_class_pre_observe_hooks: dict[
|
||||||
|
str,
|
||||||
|
Callable[
|
||||||
|
[
|
||||||
|
"AgentBase", # self
|
||||||
|
dict[str, Any], # kwargs
|
||||||
|
],
|
||||||
|
dict[str, Any] | None, # The modified kwargs or None
|
||||||
|
],
|
||||||
|
] = OrderedDict()
|
||||||
|
"""The class-level hook functions that will be called before the observe
|
||||||
|
function, which takes the `self` object and a deep copied input
|
||||||
|
arguments dictionary as input. To change the input arguments, the hook
|
||||||
|
function needs to output the modified arguments dictionary, which will be
|
||||||
|
used as the input of the next hook function or the original observe
|
||||||
|
function."""
|
||||||
|
|
||||||
|
_class_post_observe_hooks: dict[
|
||||||
|
str,
|
||||||
|
Callable[
|
||||||
|
[
|
||||||
|
"AgentBase", # self
|
||||||
|
dict[str, Any], # kwargs
|
||||||
|
None, # The output, `None` if no output
|
||||||
|
],
|
||||||
|
None,
|
||||||
|
],
|
||||||
|
] = OrderedDict()
|
||||||
|
"""The class-level hook functions that will be called after the observe
|
||||||
|
function, which takes the `self` object as input."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
"""Initialize the agent."""
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.id = shortuuid.uuid()
|
||||||
|
|
||||||
|
# The replying task and identify of the current replying
|
||||||
|
self._reply_task: Task | None = None
|
||||||
|
self._reply_id: str | None = None
|
||||||
|
|
||||||
|
# Initialize the instance-level hooks
|
||||||
|
self._instance_pre_print_hooks = OrderedDict()
|
||||||
|
self._instance_post_print_hooks = OrderedDict()
|
||||||
|
|
||||||
|
self._instance_pre_reply_hooks = OrderedDict()
|
||||||
|
self._instance_post_reply_hooks = OrderedDict()
|
||||||
|
|
||||||
|
self._instance_pre_observe_hooks = OrderedDict()
|
||||||
|
self._instance_post_observe_hooks = OrderedDict()
|
||||||
|
|
||||||
|
# The prefix used in streaming printing, which will save the
|
||||||
|
# accumulated text and audio streaming data for each message id.
|
||||||
|
# e.g. {"text": "xxx", "audio": (stream_obj, "{base64_data}")}
|
||||||
|
self._stream_prefix = {}
|
||||||
|
|
||||||
|
# The subscribers that will receive the reply message by their
|
||||||
|
# `observe` method. The key is the MsgHub id, and the value is the
|
||||||
|
# list of agents.
|
||||||
|
self._subscribers: dict[str, list[AgentBase]] = {}
|
||||||
|
|
||||||
|
# We add this variable in case developers want to disable the console
|
||||||
|
# output of the agent, e.g., in a production environment.
|
||||||
|
self._disable_console_output: bool = False
|
||||||
|
|
||||||
|
# The streaming message queue used to export the messages as a
|
||||||
|
# generator
|
||||||
|
self._disable_msg_queue: bool = True
|
||||||
|
self.msg_queue = None
|
||||||
|
|
||||||
|
async def observe(self, msg: Msg | list[Msg] | None) -> None:
|
||||||
|
"""Receive the given message(s) without generating a reply.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (`Msg | list[Msg] | None`):
|
||||||
|
The message(s) to be observed.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError(
|
||||||
|
f"The observe function is not implemented in"
|
||||||
|
f" {self.__class__.__name__} class.",
|
||||||
|
)
|
||||||
|
|
||||||
|
async def reply(self, *args: Any, **kwargs: Any) -> Msg:
|
||||||
|
"""The main logic of the agent, which generates a reply based on the
|
||||||
|
current state and input arguments."""
|
||||||
|
raise NotImplementedError(
|
||||||
|
"The reply function is not implemented in "
|
||||||
|
f"{self.__class__.__name__} class.",
|
||||||
|
)
|
||||||
|
|
||||||
|
async def print(self, msg: Msg, last: bool = True) -> None:
|
||||||
|
"""The function to display the message.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (`Msg`):
|
||||||
|
The message object to be printed.
|
||||||
|
last (`bool`, defaults to `True`):
|
||||||
|
Whether this is the last one in streaming messages. For
|
||||||
|
non-streaming message, this should always be `True`.
|
||||||
|
"""
|
||||||
|
if not self._disable_msg_queue:
|
||||||
|
await self.msg_queue.put((deepcopy(msg), last))
|
||||||
|
|
||||||
|
if self._disable_console_output:
|
||||||
|
return
|
||||||
|
|
||||||
|
# The accumulated textual content to print, including the text blocks
|
||||||
|
# and the thinking blocks
|
||||||
|
thinking_and_text_to_print = []
|
||||||
|
|
||||||
|
for block in msg.get_content_blocks():
|
||||||
|
if block["type"] == "audio":
|
||||||
|
self._process_audio_block(msg.id, block)
|
||||||
|
|
||||||
|
elif block["type"] == "text":
|
||||||
|
self._print_text_block(
|
||||||
|
msg.id,
|
||||||
|
name_prefix=msg.name,
|
||||||
|
text_content=block["text"],
|
||||||
|
thinking_and_text_to_print=thinking_and_text_to_print,
|
||||||
|
)
|
||||||
|
|
||||||
|
elif block["type"] == "thinking":
|
||||||
|
self._print_text_block(
|
||||||
|
msg.id,
|
||||||
|
name_prefix=f"{msg.name}(thinking)",
|
||||||
|
text_content=block["thinking"],
|
||||||
|
thinking_and_text_to_print=thinking_and_text_to_print,
|
||||||
|
)
|
||||||
|
|
||||||
|
elif last:
|
||||||
|
self._print_last_block(block, msg)
|
||||||
|
|
||||||
|
# Clean up resources if this is the last message in streaming
|
||||||
|
if last and msg.id in self._stream_prefix:
|
||||||
|
if "audio" in self._stream_prefix[msg.id]:
|
||||||
|
player, _ = self._stream_prefix[msg.id]["audio"]
|
||||||
|
# Close the miniaudio player
|
||||||
|
player.close()
|
||||||
|
stream_prefix = self._stream_prefix.pop(msg.id)
|
||||||
|
if "text" in stream_prefix and not stream_prefix["text"].endswith(
|
||||||
|
"\n",
|
||||||
|
):
|
||||||
|
print()
|
||||||
|
|
||||||
|
def _process_audio_block(
|
||||||
|
self,
|
||||||
|
msg_id: str,
|
||||||
|
audio_block: AudioBlock,
|
||||||
|
) -> None:
|
||||||
|
"""Process audio block content.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg_id (`str`):
|
||||||
|
The unique identifier of the message
|
||||||
|
audio_block (`AudioBlock`):
|
||||||
|
The audio content block
|
||||||
|
"""
|
||||||
|
if "source" not in audio_block:
|
||||||
|
raise ValueError(
|
||||||
|
"The audio block must contain the 'source' field.",
|
||||||
|
)
|
||||||
|
|
||||||
|
if audio_block["source"]["type"] == "url":
|
||||||
|
import urllib.request
|
||||||
|
import wave
|
||||||
|
import sounddevice as sd
|
||||||
|
|
||||||
|
url = audio_block["source"]["url"]
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(url) as response:
|
||||||
|
audio_data = response.read()
|
||||||
|
|
||||||
|
with wave.open(io.BytesIO(audio_data), "rb") as wf:
|
||||||
|
samplerate = wf.getframerate()
|
||||||
|
n_frames = wf.getnframes()
|
||||||
|
audio_frames = wf.readframes(n_frames)
|
||||||
|
|
||||||
|
# Convert byte data to numpy array
|
||||||
|
audio_np = np.frombuffer(audio_frames, dtype=np.int16)
|
||||||
|
|
||||||
|
# Play audio
|
||||||
|
sd.play(audio_np, samplerate)
|
||||||
|
sd.wait()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(
|
||||||
|
"Failed to play audio from url %s: %s",
|
||||||
|
url,
|
||||||
|
str(e),
|
||||||
|
)
|
||||||
|
|
||||||
|
elif audio_block["source"]["type"] == "base64":
|
||||||
|
data = audio_block["source"]["data"]
|
||||||
|
|
||||||
|
if msg_id not in self._stream_prefix:
|
||||||
|
self._stream_prefix[msg_id] = {}
|
||||||
|
|
||||||
|
audio_prefix = self._stream_prefix[msg_id].get("audio", None)
|
||||||
|
|
||||||
|
import sounddevice as sd
|
||||||
|
|
||||||
|
# The player and the prefix data is cached for streaming audio
|
||||||
|
if audio_prefix:
|
||||||
|
player, audio_prefix_data = audio_prefix
|
||||||
|
else:
|
||||||
|
player = sd.OutputStream(
|
||||||
|
samplerate=24000,
|
||||||
|
channels=1,
|
||||||
|
dtype=np.float32,
|
||||||
|
blocksize=1024,
|
||||||
|
latency="low",
|
||||||
|
)
|
||||||
|
player.start()
|
||||||
|
audio_prefix_data = ""
|
||||||
|
|
||||||
|
# play the audio data
|
||||||
|
new_audio_data = data[len(audio_prefix_data) :]
|
||||||
|
if new_audio_data:
|
||||||
|
audio_bytes = base64.b64decode(new_audio_data)
|
||||||
|
audio_np = np.frombuffer(audio_bytes, dtype=np.int16)
|
||||||
|
audio_float = audio_np.astype(np.float32) / 32768.0
|
||||||
|
|
||||||
|
# Write to the audio output stream
|
||||||
|
player.write(audio_float)
|
||||||
|
|
||||||
|
# save the player and the prefix data
|
||||||
|
self._stream_prefix[msg_id]["audio"] = (
|
||||||
|
player,
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
"Unsupported audio source type: "
|
||||||
|
f"{audio_block['source']['type']}",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _print_text_block(
|
||||||
|
self,
|
||||||
|
msg_id: str,
|
||||||
|
name_prefix: str,
|
||||||
|
text_content: str,
|
||||||
|
thinking_and_text_to_print: list[str],
|
||||||
|
) -> None:
|
||||||
|
"""Print the text block and thinking block content.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg_id (`str`):
|
||||||
|
The unique identifier of the message
|
||||||
|
name_prefix (`str`):
|
||||||
|
The prefix for the message, e.g. "{name}: " for text block and
|
||||||
|
"{name}(thinking): " for thinking block.
|
||||||
|
text_content (`str`):
|
||||||
|
The textual content to be printed.
|
||||||
|
thinking_and_text_to_print (`list[str]`):
|
||||||
|
A list of textual content to be printed together. Here we
|
||||||
|
gather the text and thinking blocks to print them together.
|
||||||
|
"""
|
||||||
|
thinking_and_text_to_print.append(
|
||||||
|
f"{name_prefix}: {text_content}",
|
||||||
|
)
|
||||||
|
# The accumulated text and thinking blocks to print
|
||||||
|
to_print = "\n".join(thinking_and_text_to_print)
|
||||||
|
|
||||||
|
# The text prefix that has been printed
|
||||||
|
if msg_id not in self._stream_prefix:
|
||||||
|
self._stream_prefix[msg_id] = {}
|
||||||
|
|
||||||
|
text_prefix = self._stream_prefix[msg_id].get("text", "")
|
||||||
|
|
||||||
|
# Only print when there is new text content
|
||||||
|
if len(to_print) > len(text_prefix):
|
||||||
|
print(to_print[len(text_prefix) :], end="")
|
||||||
|
|
||||||
|
# Save the printed text prefix
|
||||||
|
self._stream_prefix[msg_id]["text"] = to_print
|
||||||
|
|
||||||
|
def _print_last_block(
|
||||||
|
self,
|
||||||
|
block: ToolUseBlock | ToolResultBlock | ImageBlock | VideoBlock,
|
||||||
|
msg: Msg,
|
||||||
|
) -> None:
|
||||||
|
"""Process and print the last content block, and the block type
|
||||||
|
is not audio, text, or thinking.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
block (`ToolUseBlock | ToolResultBlock | ImageBlock | VideoBlock`):
|
||||||
|
The content block to be printed
|
||||||
|
msg (`Msg`):
|
||||||
|
The message object
|
||||||
|
"""
|
||||||
|
text_prefix = self._stream_prefix.get(msg.id, {}).get("text", "")
|
||||||
|
|
||||||
|
if text_prefix:
|
||||||
|
# Add a newline to separate from previous text content
|
||||||
|
print_newline = "" if text_prefix.endswith("\n") else "\n"
|
||||||
|
print(
|
||||||
|
f"{print_newline}"
|
||||||
|
f"{json.dumps(block, indent=4, ensure_ascii=False)}",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
f"{msg.name}:"
|
||||||
|
f" {json.dumps(block, indent=4, ensure_ascii=False)}",
|
||||||
|
)
|
||||||
|
|
||||||
|
async def __call__(self, *args: Any, **kwargs: Any) -> Msg:
|
||||||
|
"""Call the reply function with the given arguments."""
|
||||||
|
self._reply_id = shortuuid.uuid()
|
||||||
|
|
||||||
|
reply_msg: Msg | None = None
|
||||||
|
try:
|
||||||
|
self._reply_task = asyncio.current_task()
|
||||||
|
reply_msg = await self.reply(*args, **kwargs)
|
||||||
|
|
||||||
|
# The interruption is triggered by calling the interrupt method
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
reply_msg = await self.handle_interrupt(*args, **kwargs)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# Broadcast the reply message to all subscribers
|
||||||
|
if reply_msg:
|
||||||
|
await self._broadcast_to_subscribers(reply_msg)
|
||||||
|
self._reply_task = None
|
||||||
|
|
||||||
|
return reply_msg
|
||||||
|
|
||||||
|
async def _broadcast_to_subscribers(
|
||||||
|
self,
|
||||||
|
msg: Msg | list[Msg] | None,
|
||||||
|
) -> None:
|
||||||
|
"""Broadcast the message to all subscribers."""
|
||||||
|
for subscribers in self._subscribers.values():
|
||||||
|
for subscriber in subscribers:
|
||||||
|
await subscriber.observe(msg)
|
||||||
|
|
||||||
|
async def handle_interrupt(
|
||||||
|
self,
|
||||||
|
*args: Any,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> Msg:
|
||||||
|
"""The post-processing logic when the reply is interrupted by the
|
||||||
|
user or something else."""
|
||||||
|
raise NotImplementedError(
|
||||||
|
f"The handle_interrupt function is not implemented in "
|
||||||
|
f"{self.__class__.__name__}",
|
||||||
|
)
|
||||||
|
|
||||||
|
async def interrupt(self, msg: Msg | list[Msg] | None = None) -> None:
|
||||||
|
"""Interrupt the current reply process."""
|
||||||
|
if self._reply_task and not self._reply_task.done():
|
||||||
|
self._reply_task.cancel(msg)
|
||||||
|
|
||||||
|
def register_instance_hook(
|
||||||
|
self,
|
||||||
|
hook_type: AgentHookTypes,
|
||||||
|
hook_name: str,
|
||||||
|
hook: Callable,
|
||||||
|
) -> None:
|
||||||
|
"""Register a hook to the agent instance, which only takes effect
|
||||||
|
for the current instance.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
hook_type (`str`):
|
||||||
|
The type of the hook, indicating where the hook is to be
|
||||||
|
triggered.
|
||||||
|
hook_name (`str`):
|
||||||
|
The name of the hook. If the name is already registered, the
|
||||||
|
hook will be overwritten.
|
||||||
|
hook (`Callable`):
|
||||||
|
The hook function.
|
||||||
|
"""
|
||||||
|
if not isinstance(self, AgentBase):
|
||||||
|
raise TypeError(
|
||||||
|
"The register_instance_hook method should be called on an "
|
||||||
|
f"instance of AsyncAgentBase, but got {self} of "
|
||||||
|
f"type {type(self)}.",
|
||||||
|
)
|
||||||
|
hooks = getattr(self, f"_instance_{hook_type}_hooks")
|
||||||
|
hooks[hook_name] = hook
|
||||||
|
|
||||||
|
def remove_instance_hook(
|
||||||
|
self,
|
||||||
|
hook_type: AgentHookTypes,
|
||||||
|
hook_name: str,
|
||||||
|
) -> None:
|
||||||
|
"""Remove an instance-level hook from the agent instance.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
hook_type (`AgentHookTypes`):
|
||||||
|
The type of the hook, indicating where the hook is to be
|
||||||
|
triggered.
|
||||||
|
hook_name (`str`):
|
||||||
|
The name of the hook to remove.
|
||||||
|
"""
|
||||||
|
if not isinstance(self, AgentBase):
|
||||||
|
raise TypeError(
|
||||||
|
"The remove_instance_hook method should be called on an "
|
||||||
|
f"instance of AsyncAgentBase, but got {self} of "
|
||||||
|
f"type {type(self)}.",
|
||||||
|
)
|
||||||
|
hooks = getattr(self, f"_instance_{hook_type}_hooks")
|
||||||
|
if hook_name in hooks:
|
||||||
|
del hooks[hook_name]
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Hook '{hook_name}' not found in '{hook_type}' hooks of "
|
||||||
|
f"{self.__class__.__name__} instance.",
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register_class_hook(
|
||||||
|
cls,
|
||||||
|
hook_type: AgentHookTypes,
|
||||||
|
hook_name: str,
|
||||||
|
hook: Callable,
|
||||||
|
) -> None:
|
||||||
|
"""The universal function to register a hook to the agent class, which
|
||||||
|
will take effect for all instances of the class.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
hook_type (`AgentHookTypes`):
|
||||||
|
The type of the hook, indicating where the hook is to be
|
||||||
|
triggered.
|
||||||
|
hook_name (`str`):
|
||||||
|
The name of the hook. If the name is already registered, the
|
||||||
|
hook will be overwritten.
|
||||||
|
hook (`Callable`):
|
||||||
|
The hook function.
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert (
|
||||||
|
hook_type in cls.supported_hook_types
|
||||||
|
), f"Invalid hook type: {hook_type}"
|
||||||
|
|
||||||
|
hooks = getattr(cls, f"_class_{hook_type}_hooks")
|
||||||
|
hooks[hook_name] = hook
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def remove_class_hook(
|
||||||
|
cls,
|
||||||
|
hook_type: AgentHookTypes,
|
||||||
|
hook_name: str,
|
||||||
|
) -> None:
|
||||||
|
"""Remove a class-level hook from the agent class.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
hook_type (`AgentHookTypes`):
|
||||||
|
The type of the hook, indicating where the hook is to be
|
||||||
|
triggered.
|
||||||
|
hook_name (`str`):
|
||||||
|
The name of the hook to remove.
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert (
|
||||||
|
hook_type in cls.supported_hook_types
|
||||||
|
), f"Invalid hook type: {hook_type}"
|
||||||
|
hooks = getattr(cls, f"_class_{hook_type}_hooks")
|
||||||
|
if hook_name in hooks:
|
||||||
|
del hooks[hook_name]
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Hook '{hook_name}' not found in '{hook_type}' hooks of "
|
||||||
|
f"{cls.__name__} class.",
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clear_class_hooks(
|
||||||
|
cls,
|
||||||
|
hook_type: AgentHookTypes | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Clear all class-level hooks.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
hook_type (`AgentHookTypes`, optional):
|
||||||
|
The type of the hook to clear. If not specified, all
|
||||||
|
class-level hooks will be cleared.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if hook_type is None:
|
||||||
|
for typ in cls.supported_hook_types:
|
||||||
|
hooks = getattr(cls, f"_class_{typ}_hooks")
|
||||||
|
hooks.clear()
|
||||||
|
else:
|
||||||
|
assert (
|
||||||
|
hook_type in cls.supported_hook_types
|
||||||
|
), f"Invalid hook type: {hook_type}"
|
||||||
|
hooks = getattr(cls, f"_class_{hook_type}_hooks")
|
||||||
|
hooks.clear()
|
||||||
|
|
||||||
|
def clear_instance_hooks(
|
||||||
|
self,
|
||||||
|
hook_type: AgentHookTypes | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""If `hook_type` is not specified, clear all instance-level hooks.
|
||||||
|
Otherwise, clear the specified type of instance-level hooks."""
|
||||||
|
if hook_type is None:
|
||||||
|
for typ in self.supported_hook_types:
|
||||||
|
if not hasattr(self, f"_instance_{typ}_hooks"):
|
||||||
|
raise ValueError(
|
||||||
|
f"Call super().__init__() in the constructor "
|
||||||
|
f"to initialize the instance-level hooks for "
|
||||||
|
f"{self.__class__.__name__}.",
|
||||||
|
)
|
||||||
|
hooks = getattr(self, f"_instance_{typ}_hooks")
|
||||||
|
hooks.clear()
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert (
|
||||||
|
hook_type in self.supported_hook_types
|
||||||
|
), f"Invalid hook type: {hook_type}"
|
||||||
|
if not hasattr(self, f"_instance_{hook_type}_hooks"):
|
||||||
|
raise ValueError(
|
||||||
|
f"Call super().__init__() in the constructor "
|
||||||
|
f"to initialize the instance-level hooks for "
|
||||||
|
f"{self.__class__.__name__}.",
|
||||||
|
)
|
||||||
|
hooks = getattr(self, f"_instance_{hook_type}_hooks")
|
||||||
|
hooks.clear()
|
||||||
|
|
||||||
|
def reset_subscribers(
|
||||||
|
self,
|
||||||
|
msghub_name: str,
|
||||||
|
subscribers: list["AgentBase"],
|
||||||
|
) -> None:
|
||||||
|
"""Reset the subscribers of the agent.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msghub_name (`str`):
|
||||||
|
The name of the MsgHub that manages the subscribers.
|
||||||
|
subscribers (`list[AgentBase]`):
|
||||||
|
A list of agents that will receive the reply message from
|
||||||
|
this agent via their `observe` method.
|
||||||
|
"""
|
||||||
|
self._subscribers[msghub_name] = [_ for _ in subscribers if _ != self]
|
||||||
|
|
||||||
|
def remove_subscribers(self, msghub_name: str) -> None:
|
||||||
|
"""Remove the msghub subscribers by the given msg hub name.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msghub_name (`str`):
|
||||||
|
The name of the MsgHub that manages the subscribers.
|
||||||
|
"""
|
||||||
|
if msghub_name not in self._subscribers:
|
||||||
|
logger.warning(
|
||||||
|
"MsgHub named '%s' not found",
|
||||||
|
msghub_name,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._subscribers.pop(msghub_name)
|
||||||
|
|
||||||
|
@deprecated("Please use set_console_output_enabled() instead.")
|
||||||
|
def disable_console_output(self) -> None:
|
||||||
|
"""This function will disable the console output of the agent, e.g.
|
||||||
|
in a production environment to avoid messy logs."""
|
||||||
|
self._disable_console_output = True
|
||||||
|
|
||||||
|
def set_console_output_enabled(self, enabled: bool) -> None:
|
||||||
|
"""Enable or disable the console output of the agent. E.g. in a
|
||||||
|
production environment, you may want to disable the console output to
|
||||||
|
avoid messy logs.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
enabled (`bool`):
|
||||||
|
If `True`, enable the console output. If `False`, disable
|
||||||
|
the console output.
|
||||||
|
"""
|
||||||
|
self._disable_console_output = not enabled
|
||||||
|
|
||||||
|
def set_msg_queue_enabled(
|
||||||
|
self,
|
||||||
|
enabled: bool,
|
||||||
|
queue: Queue | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Enable or disable the message queue for streaming outputs.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
enabled (`bool`):
|
||||||
|
If `True`, enable the message queue to allow streaming
|
||||||
|
outputs. If `False`, disable the message queue.
|
||||||
|
queue (`Queue | None`, optional):
|
||||||
|
The queue instance that will be used to initialize the
|
||||||
|
message queue when `enable` is `True`.
|
||||||
|
"""
|
||||||
|
if enabled:
|
||||||
|
if queue is None:
|
||||||
|
if self.msg_queue is None:
|
||||||
|
self.msg_queue = asyncio.Queue(maxsize=100)
|
||||||
|
else:
|
||||||
|
self.msg_queue = queue
|
||||||
|
else:
|
||||||
|
self.msg_queue = None
|
||||||
|
|
||||||
|
self._disable_msg_queue = not enabled
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""The metaclass for agents in agentscope."""
|
||||||
|
import inspect
|
||||||
|
from copy import deepcopy
|
||||||
|
from functools import wraps
|
||||||
|
from typing import (
|
||||||
|
Any,
|
||||||
|
Dict,
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Callable,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .._utils._common import _execute_async_or_sync_func
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from ._agent_base import AgentBase
|
||||||
|
else:
|
||||||
|
AgentBase = "AgentBase"
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_to_kwargs(
|
||||||
|
func: Callable,
|
||||||
|
self: Any,
|
||||||
|
*args: Any,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> dict:
|
||||||
|
"""Normalize the provided positional and keyword arguments into a
|
||||||
|
keyword arguments dictionary that matches the function signature."""
|
||||||
|
sig = inspect.signature(func)
|
||||||
|
try:
|
||||||
|
# Bind the provided arguments to the function signature
|
||||||
|
bound = sig.bind(self, *args, **kwargs)
|
||||||
|
# Apply the default values for parameters
|
||||||
|
bound.apply_defaults()
|
||||||
|
|
||||||
|
# Return the arguments in a dictionary format
|
||||||
|
res = dict(bound.arguments)
|
||||||
|
res.pop("self")
|
||||||
|
return res
|
||||||
|
|
||||||
|
except TypeError as e:
|
||||||
|
# If failed to bind, we raise a TypeError with more context
|
||||||
|
param_names = list(sig.parameters.keys())
|
||||||
|
provided_args = len(args)
|
||||||
|
provided_kwargs = list(kwargs.keys())
|
||||||
|
|
||||||
|
raise TypeError(
|
||||||
|
f"Failed to bind parameters for function '{func.__name__}': {e}\n"
|
||||||
|
f"Expected parameters: {param_names}\n"
|
||||||
|
f"Provided {provided_args} positional args and kwargs: "
|
||||||
|
f"{provided_kwargs}",
|
||||||
|
) from e
|
||||||
|
|
||||||
|
|
||||||
|
def _wrap_with_hooks(
|
||||||
|
original_func: Callable,
|
||||||
|
) -> Callable:
|
||||||
|
"""A decorator to wrap the original async function with pre- and post-hooks
|
||||||
|
|
||||||
|
Args:
|
||||||
|
original_func (`Callable`):
|
||||||
|
The original async function to be wrapped with hooks.
|
||||||
|
"""
|
||||||
|
func_name = original_func.__name__.replace("_", "")
|
||||||
|
|
||||||
|
@wraps(original_func)
|
||||||
|
async def async_wrapper(
|
||||||
|
self: AgentBase,
|
||||||
|
*args: Any,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> Any:
|
||||||
|
"""The wrapped function, which call the pre- and post-hooks before and
|
||||||
|
after the original function."""
|
||||||
|
|
||||||
|
# Unify all positional and keyword arguments into a keyword arguments
|
||||||
|
normalized_kwargs = _normalize_to_kwargs(
|
||||||
|
original_func,
|
||||||
|
self,
|
||||||
|
*args,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
current_normalized_kwargs = normalized_kwargs
|
||||||
|
assert (
|
||||||
|
hasattr(self, f"_instance_pre_{func_name}_hooks")
|
||||||
|
and hasattr(self, f"_instance_post_{func_name}_hooks")
|
||||||
|
and hasattr(self.__class__, f"_class_pre_{func_name}_hooks")
|
||||||
|
and hasattr(self.__class__, f"_class_post_{func_name}_hooks")
|
||||||
|
), f"Hooks for {func_name} not found in {self.__class__.__name__}"
|
||||||
|
|
||||||
|
# pre-hooks
|
||||||
|
pre_hooks = list(
|
||||||
|
getattr(self, f"_instance_pre_{func_name}_hooks").values(),
|
||||||
|
) + list(
|
||||||
|
getattr(self, f"_class_pre_{func_name}_hooks").values(),
|
||||||
|
)
|
||||||
|
for pre_hook in pre_hooks:
|
||||||
|
modified_keywords = await _execute_async_or_sync_func(
|
||||||
|
pre_hook,
|
||||||
|
self,
|
||||||
|
deepcopy(current_normalized_kwargs),
|
||||||
|
)
|
||||||
|
if modified_keywords is not None:
|
||||||
|
assert isinstance(modified_keywords, dict), (
|
||||||
|
f"Pre-hook must return a dict of keyword arguments, rather"
|
||||||
|
f" than {type(modified_keywords)} from hook "
|
||||||
|
f"{pre_hook.__name__}"
|
||||||
|
)
|
||||||
|
current_normalized_kwargs = modified_keywords
|
||||||
|
|
||||||
|
# original function
|
||||||
|
# handle positional and keyword arguments specifically
|
||||||
|
args = current_normalized_kwargs.get("args", [])
|
||||||
|
kwargs = current_normalized_kwargs.get("kwargs", {})
|
||||||
|
others = {
|
||||||
|
k: v
|
||||||
|
for k, v in current_normalized_kwargs.items()
|
||||||
|
if k not in ["args", "kwargs"]
|
||||||
|
}
|
||||||
|
current_output = await original_func(
|
||||||
|
self,
|
||||||
|
*args,
|
||||||
|
**others,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
# post_hooks
|
||||||
|
post_hooks = list(
|
||||||
|
getattr(self, f"_instance_post_{func_name}_hooks").values(),
|
||||||
|
) + list(
|
||||||
|
getattr(self, f"_class_post_{func_name}_hooks").values(),
|
||||||
|
)
|
||||||
|
for post_hook in post_hooks:
|
||||||
|
modified_output = await _execute_async_or_sync_func(
|
||||||
|
post_hook,
|
||||||
|
self,
|
||||||
|
deepcopy(current_normalized_kwargs),
|
||||||
|
deepcopy(current_output),
|
||||||
|
)
|
||||||
|
if modified_output is not None:
|
||||||
|
current_output = modified_output
|
||||||
|
return current_output
|
||||||
|
|
||||||
|
return async_wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class _AgentMeta(type):
|
||||||
|
"""The agent metaclass that wraps the agent's reply, observe and print
|
||||||
|
functions with pre- and post-hooks."""
|
||||||
|
|
||||||
|
def __new__(mcs, name: Any, bases: Any, attrs: Dict) -> Any:
|
||||||
|
"""Wrap the agent's functions with hooks."""
|
||||||
|
|
||||||
|
for func_name in [
|
||||||
|
"reply",
|
||||||
|
"print",
|
||||||
|
"observe",
|
||||||
|
]:
|
||||||
|
if func_name in attrs:
|
||||||
|
attrs[func_name] = _wrap_with_hooks(attrs[func_name])
|
||||||
|
|
||||||
|
return super().__new__(mcs, name, bases, attrs)
|
||||||
|
|
||||||
|
|
||||||
|
class _ReActAgentMeta(_AgentMeta):
|
||||||
|
"""The ReAct metaclass that adds pre- and post-hooks for the _reasoning
|
||||||
|
and _acting functions."""
|
||||||
|
|
||||||
|
def __new__(mcs, name: Any, bases: Any, attrs: Dict) -> Any:
|
||||||
|
"""Wrap the ReAct agent's _reasoning and _acting functions with
|
||||||
|
hooks."""
|
||||||
|
|
||||||
|
for func_name in [
|
||||||
|
"_reasoning",
|
||||||
|
"_acting",
|
||||||
|
]:
|
||||||
|
if func_name in attrs:
|
||||||
|
attrs[func_name] = _wrap_with_hooks(attrs[func_name])
|
||||||
|
|
||||||
|
return super().__new__(mcs, name, bases, attrs)
|
||||||
@@ -0,0 +1,767 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# pylint: disable=not-an-iterable
|
||||||
|
# mypy: disable-error-code="list-item"
|
||||||
|
"""ReAct agent class in agentscope."""
|
||||||
|
import asyncio
|
||||||
|
from typing import Type, Any, AsyncGenerator, Literal
|
||||||
|
|
||||||
|
import shortuuid
|
||||||
|
from pydantic import BaseModel, ValidationError, Field
|
||||||
|
|
||||||
|
from ._react_agent_base import ReActAgentBase
|
||||||
|
from .._logging import logger
|
||||||
|
from ..formatter import FormatterBase
|
||||||
|
from ..memory import MemoryBase, LongTermMemoryBase, InMemoryMemory
|
||||||
|
from ..message import Msg, ToolUseBlock, ToolResultBlock, TextBlock
|
||||||
|
from ..model import ChatModelBase
|
||||||
|
from ..rag import KnowledgeBase, Document
|
||||||
|
from ..plan import PlanNotebook
|
||||||
|
from ..tool import Toolkit, ToolResponse
|
||||||
|
from ..tracing import trace_reply
|
||||||
|
|
||||||
|
|
||||||
|
class _QueryRewriteModel(BaseModel):
|
||||||
|
"""The structured model used for query rewriting."""
|
||||||
|
|
||||||
|
rewritten_query: str = Field(
|
||||||
|
description=(
|
||||||
|
"The rewritten query, which should be specific and concise. "
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def finish_function_pre_print_hook(
|
||||||
|
self: "ReActAgent",
|
||||||
|
kwargs: dict[str, Any],
|
||||||
|
) -> dict[str, Any] | None:
|
||||||
|
"""A pre-speak hook function that check if finish_function is called. If
|
||||||
|
so, it will wrap the response argument into a message and return it to
|
||||||
|
replace the original message. By this way, the calling of the finish
|
||||||
|
function will be displayed as a text reply instead of a tool call."""
|
||||||
|
|
||||||
|
msg = kwargs["msg"]
|
||||||
|
|
||||||
|
if isinstance(msg.content, str):
|
||||||
|
return None
|
||||||
|
|
||||||
|
if isinstance(msg.content, list):
|
||||||
|
for i, block in enumerate(msg.content):
|
||||||
|
if (
|
||||||
|
block["type"] == "tool_use"
|
||||||
|
and block["name"] == self.finish_function_name
|
||||||
|
):
|
||||||
|
# Convert the response argument into a text block for
|
||||||
|
# displaying
|
||||||
|
try:
|
||||||
|
msg.content[i] = TextBlock(
|
||||||
|
type="text",
|
||||||
|
text=block["input"].get("response", ""),
|
||||||
|
)
|
||||||
|
return kwargs
|
||||||
|
except Exception:
|
||||||
|
print("Error in block input", block["input"])
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class ReActAgent(ReActAgentBase):
|
||||||
|
"""A ReAct agent implementation in AgentScope, which supports
|
||||||
|
|
||||||
|
- Realtime steering
|
||||||
|
- API-based (parallel) tool calling
|
||||||
|
- Hooks around reasoning, acting, reply, observe and print functions
|
||||||
|
- Structured output generation
|
||||||
|
"""
|
||||||
|
|
||||||
|
finish_function_name: str = "generate_response"
|
||||||
|
"""The function name used to finish replying and return a response to
|
||||||
|
the user."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
sys_prompt: str,
|
||||||
|
model: ChatModelBase,
|
||||||
|
formatter: FormatterBase,
|
||||||
|
toolkit: Toolkit | None = None,
|
||||||
|
memory: MemoryBase | None = None,
|
||||||
|
long_term_memory: LongTermMemoryBase | None = None,
|
||||||
|
long_term_memory_mode: Literal[
|
||||||
|
"agent_control",
|
||||||
|
"static_control",
|
||||||
|
"both",
|
||||||
|
] = "both",
|
||||||
|
enable_meta_tool: bool = False,
|
||||||
|
parallel_tool_calls: bool = False,
|
||||||
|
knowledge: KnowledgeBase | list[KnowledgeBase] | None = None,
|
||||||
|
enable_rewrite_query: bool = True,
|
||||||
|
plan_notebook: PlanNotebook | None = None,
|
||||||
|
print_hint_msg: bool = False,
|
||||||
|
max_iters: int = 10,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the ReAct agent
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (`str`):
|
||||||
|
The name of the agent.
|
||||||
|
sys_prompt (`str`):
|
||||||
|
The system prompt of the agent.
|
||||||
|
model (`ChatModelBase`):
|
||||||
|
The chat model used by the agent.
|
||||||
|
formatter (`FormatterBase`):
|
||||||
|
The formatter used to format the messages into the required
|
||||||
|
format of the model API provider.
|
||||||
|
toolkit (`Toolkit | None`, optional):
|
||||||
|
A `Toolkit` object that contains the tool functions. If not
|
||||||
|
provided, a default empty `Toolkit` will be created.
|
||||||
|
memory (`MemoryBase | None`, optional):
|
||||||
|
The memory used to store the dialogue history. If not provided,
|
||||||
|
a default `InMemoryMemory` will be created, which stores
|
||||||
|
messages in a list in memory.
|
||||||
|
long_term_memory (`LongTermMemoryBase | None`, optional):
|
||||||
|
The optional long-term memory, which will provide two tool
|
||||||
|
functions: `retrieve_from_memory` and `record_to_memory`, and
|
||||||
|
will attach the retrieved information to the system prompt
|
||||||
|
before each reply.
|
||||||
|
enable_meta_tool (`bool`, defaults to `False`):
|
||||||
|
If `True`, a meta tool function `reset_equipped_tools` will be
|
||||||
|
added to the toolkit, which allows the agent to manage its
|
||||||
|
equipped tools dynamically.
|
||||||
|
long_term_memory_mode (`Literal['agent_control', 'static_control',\
|
||||||
|
'both']`, defaults to `both`):
|
||||||
|
The mode of the long-term memory. If `agent_control`, two
|
||||||
|
tool functions `retrieve_from_memory` and `record_to_memory`
|
||||||
|
will be registered in the toolkit to allow the agent to
|
||||||
|
manage the long-term memory. If `static_control`, retrieving
|
||||||
|
and recording will happen in the beginning and end of
|
||||||
|
each reply respectively.
|
||||||
|
parallel_tool_calls (`bool`, defaults to `False`):
|
||||||
|
When LLM generates multiple tool calls, whether to execute
|
||||||
|
them in parallel.
|
||||||
|
knowledge (`KnowledgeBase | list[KnowledgeBase] | None`, optional):
|
||||||
|
The knowledge object(s) used by the agent to retrieve
|
||||||
|
relevant documents at the beginning of each reply.
|
||||||
|
enable_rewrite_query (`bool`, defaults to `True`):
|
||||||
|
Whether ask the agent to rewrite the user input query before
|
||||||
|
retrieving from the knowledge base(s), e.g. rewrite "Who am I"
|
||||||
|
to "{user's name}" to get more relevant documents. Only works
|
||||||
|
when the knowledge base(s) is provided.
|
||||||
|
plan_notebook (`PlanNotebook | None`, optional):
|
||||||
|
The plan notebook instance, allow the agent to finish the
|
||||||
|
complex task by decomposing it into a sequence of subtasks.
|
||||||
|
print_hint_msg (`bool`, defaults to `False`):
|
||||||
|
Whether to print the hint messages, including the reasoning
|
||||||
|
hint from the plan notebook, the retrieved information from
|
||||||
|
the long-term memory and knowledge base(s).
|
||||||
|
max_iters (`int`, defaults to `10`):
|
||||||
|
The maximum number of iterations of the reasoning-acting loops.
|
||||||
|
"""
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
assert long_term_memory_mode in [
|
||||||
|
"agent_control",
|
||||||
|
"static_control",
|
||||||
|
"both",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Static variables in the agent
|
||||||
|
self.name = name
|
||||||
|
self._sys_prompt = sys_prompt
|
||||||
|
self.max_iters = max_iters
|
||||||
|
self.model = model
|
||||||
|
self.formatter = formatter
|
||||||
|
|
||||||
|
# -------------- Memory management --------------
|
||||||
|
# Record the dialogue history in the memory
|
||||||
|
self.memory = memory or InMemoryMemory()
|
||||||
|
# If provide the long-term memory, it will be used to retrieve info
|
||||||
|
# in the beginning of each reply, and the result will be added to the
|
||||||
|
# system prompt
|
||||||
|
self.long_term_memory = long_term_memory
|
||||||
|
|
||||||
|
# The long-term memory mode
|
||||||
|
self._static_control = long_term_memory and long_term_memory_mode in [
|
||||||
|
"static_control",
|
||||||
|
"both",
|
||||||
|
]
|
||||||
|
self._agent_control = long_term_memory and long_term_memory_mode in [
|
||||||
|
"agent_control",
|
||||||
|
"both",
|
||||||
|
]
|
||||||
|
|
||||||
|
# -------------- Tool management --------------
|
||||||
|
# If None, a default Toolkit will be created
|
||||||
|
self.toolkit = toolkit or Toolkit()
|
||||||
|
self.toolkit.register_tool_function(
|
||||||
|
getattr(self, self.finish_function_name),
|
||||||
|
)
|
||||||
|
if self._agent_control:
|
||||||
|
# Adding two tool functions into the toolkit to allow self-control
|
||||||
|
self.toolkit.register_tool_function(
|
||||||
|
long_term_memory.record_to_memory,
|
||||||
|
)
|
||||||
|
self.toolkit.register_tool_function(
|
||||||
|
long_term_memory.retrieve_from_memory,
|
||||||
|
)
|
||||||
|
# Add a meta tool function to allow agent-controlled tool management
|
||||||
|
if enable_meta_tool or plan_notebook:
|
||||||
|
self.toolkit.register_tool_function(
|
||||||
|
self.toolkit.reset_equipped_tools,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.parallel_tool_calls = parallel_tool_calls
|
||||||
|
|
||||||
|
# -------------- RAG management --------------
|
||||||
|
# The knowledge base(s) used by the agent
|
||||||
|
if isinstance(knowledge, KnowledgeBase):
|
||||||
|
knowledge = [knowledge]
|
||||||
|
self.knowledge: list[KnowledgeBase] = knowledge or []
|
||||||
|
self.enable_rewrite_query = enable_rewrite_query
|
||||||
|
|
||||||
|
# -------------- Plan management --------------
|
||||||
|
# Equipped the plan-related tools provided by the plan notebook as
|
||||||
|
# a tool group named "plan_related". So that the agent can activate
|
||||||
|
# the plan tools by the meta tool function
|
||||||
|
self.plan_notebook = None
|
||||||
|
if plan_notebook:
|
||||||
|
self.plan_notebook = plan_notebook
|
||||||
|
# When enable_meta_tool is True, plan tools are in plan_related
|
||||||
|
# group and active by agent.
|
||||||
|
# Otherwise, plan tools in bassic group and always active.
|
||||||
|
if enable_meta_tool:
|
||||||
|
self.toolkit.create_tool_group(
|
||||||
|
"plan_related",
|
||||||
|
description=self.plan_notebook.description,
|
||||||
|
)
|
||||||
|
for tool in plan_notebook.list_tools():
|
||||||
|
self.toolkit.register_tool_function(
|
||||||
|
tool,
|
||||||
|
group_name="plan_related",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
for tool in plan_notebook.list_tools():
|
||||||
|
self.toolkit.register_tool_function(
|
||||||
|
tool,
|
||||||
|
)
|
||||||
|
|
||||||
|
# If print the reasoning hint messages
|
||||||
|
self.print_hint_msg = print_hint_msg
|
||||||
|
|
||||||
|
# The maximum number of iterations of the reasoning-acting loops
|
||||||
|
self.max_iters = max_iters
|
||||||
|
|
||||||
|
# The hint messages that will be attached to the prompt to guide the
|
||||||
|
# agent's behavior before each reasoning step, and cleared after
|
||||||
|
# each reasoning step, meaning the hint messages is one-time use only.
|
||||||
|
# We use an InMemoryMemory instance to store the hint messages
|
||||||
|
self._reasoning_hint_msgs = InMemoryMemory()
|
||||||
|
|
||||||
|
# Variables to record the intermediate state
|
||||||
|
|
||||||
|
# If required structured output model is provided
|
||||||
|
self._required_structured_model: Type[BaseModel] | None = None
|
||||||
|
|
||||||
|
# -------------- State registration and hooks --------------
|
||||||
|
# Register the status variables
|
||||||
|
self.register_state("name")
|
||||||
|
self.register_state("_sys_prompt")
|
||||||
|
|
||||||
|
self.register_instance_hook(
|
||||||
|
"pre_print",
|
||||||
|
"finish_function_pre_print_hook",
|
||||||
|
finish_function_pre_print_hook,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sys_prompt(self) -> str:
|
||||||
|
"""The dynamic system prompt of the agent."""
|
||||||
|
return self._sys_prompt
|
||||||
|
|
||||||
|
@trace_reply
|
||||||
|
async def reply(
|
||||||
|
self,
|
||||||
|
msg: Msg | list[Msg] | None = None,
|
||||||
|
structured_model: Type[BaseModel] | None = None,
|
||||||
|
) -> Msg:
|
||||||
|
"""Generate a reply based on the current state and input arguments.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (`Msg | list[Msg] | None`, optional):
|
||||||
|
The input message(s) to the agent.
|
||||||
|
structured_model (`Type[BaseModel] | None`, optional):
|
||||||
|
The required structured output model. If provided, the agent
|
||||||
|
is expected to generate structured output in the `metadata`
|
||||||
|
field of the output message.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
`Msg`:
|
||||||
|
The output message generated by the agent.
|
||||||
|
"""
|
||||||
|
# Record the input message(s) in the memory
|
||||||
|
await self.memory.add(msg)
|
||||||
|
|
||||||
|
# Retrieve relevant records from the long-term memory if activated
|
||||||
|
await self._retrieve_from_long_term_memory(msg)
|
||||||
|
# Retrieve relevant documents from the knowledge base(s) if any
|
||||||
|
await self._retrieve_from_knowledge(msg)
|
||||||
|
|
||||||
|
self._required_structured_model = structured_model
|
||||||
|
# Record structured output model if provided
|
||||||
|
if structured_model:
|
||||||
|
self.toolkit.set_extended_model(
|
||||||
|
self.finish_function_name,
|
||||||
|
structured_model,
|
||||||
|
)
|
||||||
|
|
||||||
|
# The reasoning-acting loop
|
||||||
|
reply_msg = None
|
||||||
|
for _ in range(self.max_iters):
|
||||||
|
msg_reasoning = await self._reasoning()
|
||||||
|
|
||||||
|
futures = [
|
||||||
|
self._acting(tool_call)
|
||||||
|
for tool_call in msg_reasoning.get_content_blocks(
|
||||||
|
"tool_use",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Parallel tool calls or not
|
||||||
|
if self.parallel_tool_calls:
|
||||||
|
acting_responses = await asyncio.gather(*futures)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Sequential tool calls
|
||||||
|
acting_responses = [await _ for _ in futures]
|
||||||
|
|
||||||
|
# Find the first non-None replying message from the acting
|
||||||
|
for acting_msg in acting_responses:
|
||||||
|
reply_msg = reply_msg or acting_msg
|
||||||
|
|
||||||
|
if reply_msg:
|
||||||
|
break
|
||||||
|
|
||||||
|
# When the maximum iterations are reached
|
||||||
|
if reply_msg is None:
|
||||||
|
reply_msg = await self._summarizing()
|
||||||
|
|
||||||
|
# Post-process the memory, long-term memory
|
||||||
|
if self._static_control:
|
||||||
|
await self.long_term_memory.record(
|
||||||
|
[
|
||||||
|
*([*msg] if isinstance(msg, list) else [msg]),
|
||||||
|
*await self.memory.get_memory(),
|
||||||
|
reply_msg,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
await self.memory.add(reply_msg)
|
||||||
|
return reply_msg
|
||||||
|
|
||||||
|
async def _reasoning(
|
||||||
|
self,
|
||||||
|
) -> Msg:
|
||||||
|
"""Perform the reasoning process."""
|
||||||
|
if self.plan_notebook:
|
||||||
|
# Insert the reasoning hint from the plan notebook
|
||||||
|
hint_msg = await self.plan_notebook.get_current_hint()
|
||||||
|
if self.print_hint_msg and hint_msg:
|
||||||
|
await self.print(hint_msg)
|
||||||
|
await self._reasoning_hint_msgs.add(hint_msg)
|
||||||
|
|
||||||
|
# Convert Msg objects into the required format of the model API
|
||||||
|
prompt = await self.formatter.format(
|
||||||
|
msgs=[
|
||||||
|
Msg("system", self.sys_prompt, "system"),
|
||||||
|
*await self.memory.get_memory(),
|
||||||
|
# The hint messages to guide the agent's behavior, maybe empty
|
||||||
|
*await self._reasoning_hint_msgs.get_memory(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
# Clear the hint messages after use
|
||||||
|
await self._reasoning_hint_msgs.clear()
|
||||||
|
|
||||||
|
res = await self.model(
|
||||||
|
prompt,
|
||||||
|
tools=self.toolkit.get_json_schemas(),
|
||||||
|
)
|
||||||
|
|
||||||
|
# handle output from the model
|
||||||
|
interrupted_by_user = False
|
||||||
|
msg = None
|
||||||
|
try:
|
||||||
|
if self.model.stream:
|
||||||
|
msg = Msg(self.name, [], "assistant")
|
||||||
|
async for content_chunk in res:
|
||||||
|
msg.content = content_chunk.content
|
||||||
|
await self.print(msg, False)
|
||||||
|
await self.print(msg, True)
|
||||||
|
|
||||||
|
# Add a tiny sleep to yield the last message object in the
|
||||||
|
# message queue
|
||||||
|
await asyncio.sleep(0.001)
|
||||||
|
|
||||||
|
else:
|
||||||
|
msg = Msg(self.name, list(res.content), "assistant")
|
||||||
|
await self.print(msg, True)
|
||||||
|
|
||||||
|
except asyncio.CancelledError as e:
|
||||||
|
interrupted_by_user = True
|
||||||
|
raise e from None
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if msg and not msg.has_content_blocks("tool_use"):
|
||||||
|
# Turn plain text response into a tool call of the finish
|
||||||
|
# function
|
||||||
|
msg = Msg.from_dict(msg.to_dict())
|
||||||
|
msg.content = [
|
||||||
|
ToolUseBlock(
|
||||||
|
id=shortuuid.uuid(),
|
||||||
|
type="tool_use",
|
||||||
|
name=self.finish_function_name,
|
||||||
|
input={"response": msg.get_text_content()},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
# None will be ignored by the memory
|
||||||
|
await self.memory.add(msg)
|
||||||
|
|
||||||
|
# Post-process for user interruption
|
||||||
|
if interrupted_by_user and msg:
|
||||||
|
# Fake tool results
|
||||||
|
tool_use_blocks: list = msg.get_content_blocks(
|
||||||
|
"tool_use",
|
||||||
|
)
|
||||||
|
for tool_call in tool_use_blocks:
|
||||||
|
msg_res = Msg(
|
||||||
|
"system",
|
||||||
|
[
|
||||||
|
ToolResultBlock(
|
||||||
|
type="tool_result",
|
||||||
|
id=tool_call["id"],
|
||||||
|
name=tool_call["name"],
|
||||||
|
output="The tool call has been interrupted "
|
||||||
|
"by the user.",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
"system",
|
||||||
|
)
|
||||||
|
await self.memory.add(msg_res)
|
||||||
|
await self.print(msg_res, True)
|
||||||
|
return msg
|
||||||
|
|
||||||
|
async def _acting(self, tool_call: ToolUseBlock) -> Msg | None:
|
||||||
|
"""Perform the acting process.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tool_call (`ToolUseBlock`):
|
||||||
|
The tool use block to be executed.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
`Union[Msg, None]`:
|
||||||
|
Return a message to the user if the `finish_function` is
|
||||||
|
called, otherwise return `None`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
tool_res_msg = Msg(
|
||||||
|
"system",
|
||||||
|
[
|
||||||
|
ToolResultBlock(
|
||||||
|
type="tool_result",
|
||||||
|
id=tool_call["id"],
|
||||||
|
name=tool_call["name"],
|
||||||
|
output=[],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
"system",
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
# Execute the tool call
|
||||||
|
tool_res = await self.toolkit.call_tool_function(tool_call)
|
||||||
|
|
||||||
|
response_msg = None
|
||||||
|
# Async generator handling
|
||||||
|
async for chunk in tool_res:
|
||||||
|
# Turn into a tool result block
|
||||||
|
tool_res_msg.content[0][ # type: ignore[index]
|
||||||
|
"output"
|
||||||
|
] = chunk.content
|
||||||
|
|
||||||
|
# Skip the printing of the finish function call
|
||||||
|
if (
|
||||||
|
tool_call["name"] != self.finish_function_name
|
||||||
|
or tool_call["name"] == self.finish_function_name
|
||||||
|
and (
|
||||||
|
chunk.metadata is None
|
||||||
|
or not chunk.metadata.get("success")
|
||||||
|
)
|
||||||
|
):
|
||||||
|
await self.print(tool_res_msg, chunk.is_last)
|
||||||
|
|
||||||
|
# Raise the CancelledError to handle the interruption in the
|
||||||
|
# handle_interrupt function
|
||||||
|
if chunk.is_interrupted:
|
||||||
|
raise asyncio.CancelledError()
|
||||||
|
|
||||||
|
# Return message if generate_response is called successfully
|
||||||
|
if (
|
||||||
|
tool_call["name"] == self.finish_function_name
|
||||||
|
and chunk.metadata
|
||||||
|
and chunk.metadata.get(
|
||||||
|
"success",
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
):
|
||||||
|
response_msg = chunk.metadata.get("response_msg")
|
||||||
|
|
||||||
|
return response_msg
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# Record the tool result message in the memory
|
||||||
|
await self.memory.add(tool_res_msg)
|
||||||
|
|
||||||
|
async def observe(self, msg: Msg | list[Msg] | None) -> None:
|
||||||
|
"""Receive observing message(s) without generating a reply.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (`Msg | list[Msg] | None`):
|
||||||
|
The message or messages to be observed.
|
||||||
|
"""
|
||||||
|
await self.memory.add(msg)
|
||||||
|
|
||||||
|
async def _summarizing(self) -> Msg:
|
||||||
|
"""Generate a response when the agent fails to solve the problem in
|
||||||
|
the maximum iterations."""
|
||||||
|
hint_msg = Msg(
|
||||||
|
"user",
|
||||||
|
"You have failed to generate response within the maximum "
|
||||||
|
"iterations. Now respond directly by summarizing the current "
|
||||||
|
"situation.",
|
||||||
|
role="user",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Generate a reply by summarizing the current situation
|
||||||
|
prompt = await self.formatter.format(
|
||||||
|
[
|
||||||
|
Msg("system", self.sys_prompt, "system"),
|
||||||
|
*await self.memory.get_memory(),
|
||||||
|
hint_msg,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
# TODO: handle the structured output here, maybe force calling the
|
||||||
|
# finish_function here
|
||||||
|
res = await self.model(prompt)
|
||||||
|
|
||||||
|
res_msg = Msg(self.name, [], "assistant")
|
||||||
|
if isinstance(res, AsyncGenerator):
|
||||||
|
async for chunk in res:
|
||||||
|
res_msg.content = chunk.content
|
||||||
|
await self.print(res_msg, False)
|
||||||
|
await self.print(res_msg, True)
|
||||||
|
|
||||||
|
else:
|
||||||
|
res_msg.content = res.content
|
||||||
|
await self.print(res_msg, True)
|
||||||
|
|
||||||
|
return res_msg
|
||||||
|
|
||||||
|
async def handle_interrupt(
|
||||||
|
self,
|
||||||
|
_msg: Msg | list[Msg] | None = None,
|
||||||
|
) -> Msg:
|
||||||
|
"""The post-processing logic when the reply is interrupted by the
|
||||||
|
user or something else."""
|
||||||
|
|
||||||
|
response_msg = Msg(
|
||||||
|
self.name,
|
||||||
|
"I noticed that you have interrupted me. What can I "
|
||||||
|
"do for you?",
|
||||||
|
"assistant",
|
||||||
|
metadata={
|
||||||
|
# Expose this field to indicate the interruption
|
||||||
|
"is_interrupted": True,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await self.print(response_msg, True)
|
||||||
|
await self.memory.add(response_msg)
|
||||||
|
return response_msg
|
||||||
|
|
||||||
|
def generate_response(
|
||||||
|
self,
|
||||||
|
response: str,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> ToolResponse:
|
||||||
|
"""Generate a response. Note only the input argument `response` is
|
||||||
|
visible to the others, you should include all the necessary
|
||||||
|
information in the `response` argument.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
response (`str`):
|
||||||
|
Your response to the user.
|
||||||
|
"""
|
||||||
|
response_msg = Msg(
|
||||||
|
self.name,
|
||||||
|
response,
|
||||||
|
"assistant",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Prepare structured output
|
||||||
|
if self._required_structured_model:
|
||||||
|
try:
|
||||||
|
# Use the metadata field of the message to store the
|
||||||
|
# structured output
|
||||||
|
response_msg.metadata = (
|
||||||
|
self._required_structured_model.model_validate(
|
||||||
|
kwargs,
|
||||||
|
).model_dump()
|
||||||
|
)
|
||||||
|
|
||||||
|
except ValidationError as e:
|
||||||
|
return ToolResponse(
|
||||||
|
content=[
|
||||||
|
TextBlock(
|
||||||
|
type="text",
|
||||||
|
text=f"Arguments Validation Error: {e}",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
metadata={
|
||||||
|
"success": False,
|
||||||
|
"response_msg": None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return ToolResponse(
|
||||||
|
content=[
|
||||||
|
TextBlock(
|
||||||
|
type="text",
|
||||||
|
text="Successfully generated response.",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
metadata={
|
||||||
|
"success": True,
|
||||||
|
"response_msg": response_msg,
|
||||||
|
},
|
||||||
|
is_last=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _retrieve_from_long_term_memory(
|
||||||
|
self,
|
||||||
|
msg: Msg | list[Msg] | None,
|
||||||
|
) -> None:
|
||||||
|
"""Insert the retrieved information from the long-term memory into
|
||||||
|
the short-term memory as a Msg object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (`Msg | list[Msg] | None`):
|
||||||
|
The input message to the agent.
|
||||||
|
"""
|
||||||
|
if self._static_control and msg:
|
||||||
|
# Retrieve information from the long-term memory if available
|
||||||
|
retrieved_info = await self.long_term_memory.retrieve(msg)
|
||||||
|
if retrieved_info:
|
||||||
|
retrieved_msg = Msg(
|
||||||
|
name="long_term_memory",
|
||||||
|
content="<long_term_memory>The content below are "
|
||||||
|
"retrieved from long-term memory, which maybe "
|
||||||
|
f"useful:\n{retrieved_info}</long_term_memory>",
|
||||||
|
role="user",
|
||||||
|
)
|
||||||
|
if self.print_hint_msg:
|
||||||
|
await self.print(retrieved_msg, True)
|
||||||
|
await self.memory.add(retrieved_msg)
|
||||||
|
|
||||||
|
async def _retrieve_from_knowledge(
|
||||||
|
self,
|
||||||
|
msg: Msg | list[Msg] | None,
|
||||||
|
) -> None:
|
||||||
|
"""Insert the retrieved documents from the RAG knowledge base(s) if
|
||||||
|
available.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg (`Msg | list[Msg] | None`):
|
||||||
|
The input message to the agent.
|
||||||
|
"""
|
||||||
|
if self.knowledge and msg:
|
||||||
|
# Prepare the user input query
|
||||||
|
query = None
|
||||||
|
if isinstance(msg, Msg):
|
||||||
|
query = msg.get_text_content()
|
||||||
|
elif isinstance(msg, list):
|
||||||
|
query = "\n".join(_.get_text_content() for _ in msg)
|
||||||
|
|
||||||
|
# Skip if the query is empty
|
||||||
|
if not query:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Rewrite the query by the LLM if enabled
|
||||||
|
if self.enable_rewrite_query:
|
||||||
|
try:
|
||||||
|
rewrite_prompt = await self.formatter.format(
|
||||||
|
msgs=[
|
||||||
|
Msg("system", self.sys_prompt, "system"),
|
||||||
|
*await self.memory.get_memory(),
|
||||||
|
Msg(
|
||||||
|
"user",
|
||||||
|
"<system-hint>Now you need to rewrite "
|
||||||
|
"the above user query to be more specific and "
|
||||||
|
"concise for knowledge retrieval. For "
|
||||||
|
"example, rewrite the query 'what happened "
|
||||||
|
"last day' to 'what happened on 2023-10-01' "
|
||||||
|
"(assuming today is 2023-10-02)."
|
||||||
|
"</system-hint>",
|
||||||
|
"user",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
stream_tmp = self.model.stream
|
||||||
|
self.model.stream = False
|
||||||
|
res = await self.model(
|
||||||
|
rewrite_prompt,
|
||||||
|
structured_model=_QueryRewriteModel,
|
||||||
|
)
|
||||||
|
self.model.stream = stream_tmp
|
||||||
|
if res.metadata and res.metadata.get("rewritten_query"):
|
||||||
|
query = res.metadata["rewritten_query"]
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(
|
||||||
|
"Skipping the query rewriting due to error: %s",
|
||||||
|
str(e),
|
||||||
|
)
|
||||||
|
|
||||||
|
docs: list[Document] = []
|
||||||
|
for kb in self.knowledge:
|
||||||
|
# retrieve the user input query
|
||||||
|
docs.extend(
|
||||||
|
await kb.retrieve(query=query),
|
||||||
|
)
|
||||||
|
if docs:
|
||||||
|
# Rerank by the relevance score
|
||||||
|
docs = sorted(
|
||||||
|
docs,
|
||||||
|
key=lambda doc: doc.score or 0.0,
|
||||||
|
reverse=True,
|
||||||
|
)
|
||||||
|
# Prepare the retrieved knowledge string
|
||||||
|
retrieved_msg = Msg(
|
||||||
|
name="user",
|
||||||
|
content=[
|
||||||
|
TextBlock(
|
||||||
|
type="text",
|
||||||
|
text=(
|
||||||
|
"<retrieved_knowledge>Use the following "
|
||||||
|
"content from the knowledge base(s) if it's "
|
||||||
|
"helpful:\n"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
*[_.metadata.content for _ in docs],
|
||||||
|
TextBlock(
|
||||||
|
type="text",
|
||||||
|
text="</retrieved_knowledge>",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
role="user",
|
||||||
|
)
|
||||||
|
if self.print_hint_msg:
|
||||||
|
await self.print(retrieved_msg, True)
|
||||||
|
await self.memory.add(retrieved_msg)
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""The base class for ReAct agent in agentscope."""
|
||||||
|
from abc import abstractmethod
|
||||||
|
from collections import OrderedDict
|
||||||
|
from typing import Callable, Any
|
||||||
|
|
||||||
|
from ._agent_base import AgentBase
|
||||||
|
from ._agent_meta import _ReActAgentMeta
|
||||||
|
from ..message import Msg
|
||||||
|
|
||||||
|
|
||||||
|
class ReActAgentBase(AgentBase, metaclass=_ReActAgentMeta):
|
||||||
|
"""The ReAct agent base class.
|
||||||
|
|
||||||
|
To support ReAct algorithm, this class extends the AgentBase class by
|
||||||
|
adding two abstract interfaces: reasoning and acting, while supporting
|
||||||
|
hook functions at four positions: pre-reasoning, post-reasoning,
|
||||||
|
pre-acting, and post-acting by the `_ReActAgentMeta` metaclass.
|
||||||
|
"""
|
||||||
|
|
||||||
|
supported_hook_types: list[str] = [
|
||||||
|
"pre_reply",
|
||||||
|
"post_reply",
|
||||||
|
"pre_print",
|
||||||
|
"post_print",
|
||||||
|
"pre_observe",
|
||||||
|
"post_observe",
|
||||||
|
"pre_reasoning",
|
||||||
|
"post_reasoning",
|
||||||
|
"pre_acting",
|
||||||
|
"post_acting",
|
||||||
|
]
|
||||||
|
"""Supported hook types for the agent base class."""
|
||||||
|
|
||||||
|
_class_pre_reasoning_hooks: dict[
|
||||||
|
str,
|
||||||
|
Callable[
|
||||||
|
[
|
||||||
|
"ReActAgentBase", # self
|
||||||
|
dict[str, Any], # kwargs
|
||||||
|
],
|
||||||
|
dict[str, Any] | None, # The modified kwargs or None
|
||||||
|
],
|
||||||
|
] = OrderedDict()
|
||||||
|
"""The class-level pre-reasoning hooks, taking `self` object, the input
|
||||||
|
arguments as input"""
|
||||||
|
|
||||||
|
_class_post_reasoning_hooks: dict[
|
||||||
|
str,
|
||||||
|
Callable[
|
||||||
|
[
|
||||||
|
"ReActAgentBase", # self
|
||||||
|
dict[str, Any], # kwargs
|
||||||
|
Any, # output
|
||||||
|
],
|
||||||
|
Msg | None, # the modified output message or None
|
||||||
|
],
|
||||||
|
] = OrderedDict()
|
||||||
|
"""The class-level post-reasoning hooks, taking `self` object, the input
|
||||||
|
arguments and the output message as input, and return the modified output
|
||||||
|
message or None if no modification is needed."""
|
||||||
|
|
||||||
|
_class_pre_acting_hooks: dict[
|
||||||
|
str,
|
||||||
|
Callable[
|
||||||
|
[
|
||||||
|
"ReActAgentBase", # self
|
||||||
|
dict[str, Any], # kwargs
|
||||||
|
],
|
||||||
|
dict[str, Any] | None, # The modified kwargs or None
|
||||||
|
],
|
||||||
|
] = OrderedDict()
|
||||||
|
"""The class-level pre-acting hooks, taking `self` object, the input
|
||||||
|
arguments as input, and return the modified input arguments or None if no
|
||||||
|
modification is needed."""
|
||||||
|
|
||||||
|
_class_post_acting_hooks: dict[
|
||||||
|
str,
|
||||||
|
Callable[
|
||||||
|
[
|
||||||
|
"ReActAgentBase", # self
|
||||||
|
dict[str, Any], # kwargs
|
||||||
|
Any, # output
|
||||||
|
],
|
||||||
|
Msg | None, # the modified output message or None
|
||||||
|
],
|
||||||
|
] = OrderedDict()
|
||||||
|
"""The class-level post-acting hooks, taking `self` object, the input
|
||||||
|
arguments and the output message as input, and return the modified output
|
||||||
|
message or None if no modification is needed."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the ReAct agent base class."""
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
# Init reasoning and acting hooks
|
||||||
|
self._instance_pre_reasoning_hooks = OrderedDict()
|
||||||
|
self._instance_post_reasoning_hooks = OrderedDict()
|
||||||
|
self._instance_pre_acting_hooks = OrderedDict()
|
||||||
|
self._instance_post_acting_hooks = OrderedDict()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def _reasoning(
|
||||||
|
self,
|
||||||
|
*args: Any,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> Any:
|
||||||
|
"""The reasoning process of the ReAct agent, which will be wrapped
|
||||||
|
with pre- and post-hooks."""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def _acting(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
"""The acting process of the ReAct agent, which will be wrapped with
|
||||||
|
pre- and post-hooks."""
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user