agent-assembler 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+
2
+ from .assembler import Assembler
3
+ from .recipe import Recipe
4
+
5
+ __version__ = "0.2.0.dev1"
6
+
7
+ __all__ = ["Assembler", "Recipe"]
@@ -0,0 +1,5 @@
1
+ from .base import BaseAdapter
2
+ from .coze import CozeAdapter
3
+ from .qianwen import QianwenAdapter
4
+
5
+ __all__ = ["BaseAdapter", "CozeAdapter", "QianwenAdapter"]
@@ -0,0 +1,23 @@
1
+
2
+ from abc import ABC, abstractmethod
3
+ from typing import Any, Dict, List
4
+ from ..recipe import Recipe
5
+
6
+ class BaseAdapter(ABC):
7
+ """Base class for all platform adapters."""
8
+
9
+ PLATFORM_NAME = "Base"
10
+
11
+ @abstractmethod
12
+ def export(self, recipe: Recipe) -> Dict[str, Any]:
13
+ """Export recipe to target platform format."""
14
+ pass
15
+
16
+ @abstractmethod
17
+ def validate(self, recipe: Recipe) -> List[str]:
18
+ """Validate recipe against platform constraints."""
19
+ pass
20
+
21
+ def deploy(self, recipe: Recipe) -> bool:
22
+ """Deploy agent to platform."""
23
+ raise NotImplementedError("Deploy not implemented.")
@@ -0,0 +1,91 @@
1
+ from typing import Any, Dict, List, Optional
2
+ from ..recipe import Recipe
3
+ from .base import BaseAdapter
4
+ import os
5
+
6
+
7
+ class CozeAdapter(BaseAdapter):
8
+ """Map Agent Assembler Recipe to Coze Bot Configuration.
9
+
10
+ Coze Bots require:
11
+ - bot_info.name
12
+ - bot_info.description
13
+ - bot_info.prompt_info.prompt (system instruction)
14
+ - bot_info.onboarding_info.prologue (welcome message)
15
+
16
+ This adapter injects Recipe + Skills content into the Coze DSL format.
17
+ """
18
+
19
+ PLATFORM_NAME = "Coze"
20
+
21
+ def __init__(self, skills_dir: Optional[str] = None):
22
+ """Initialize with optional skills directory for JIT loading."""
23
+ self.skills_dir = skills_dir
24
+
25
+ def _load_skills(self, recipe: Recipe) -> str:
26
+ """Load skill contents and format as prompt section."""
27
+ if not self.skills_dir:
28
+ return "(Skills directory not configured)"
29
+
30
+ sections = []
31
+ for skill_name in recipe.skills:
32
+ skill_path = os.path.join(self.skills_dir, skill_name, "SKILL.md")
33
+ if os.path.exists(skill_path):
34
+ with open(skill_path, "r", encoding="utf-8") as f:
35
+ sections.append(f"### Skill: {skill_name}\n{f.read()}")
36
+ else:
37
+ sections.append(f"### Skill: {skill_name}\n[Warning: Skill file not found]")
38
+
39
+ return "\n\n".join(sections)
40
+
41
+ def export(self, recipe: Recipe) -> Dict[str, Any]:
42
+ """Export recipe as Coze-compatible bot configuration."""
43
+ # Build system prompt
44
+ system_prompt = f"# Role\nYou are an AI assistant specialized in **{recipe.name}**.\n\n"
45
+
46
+ if recipe.notes:
47
+ system_prompt += f"## Context\n{recipe.notes}\n\n"
48
+
49
+ system_prompt += "## Skills & Instructions\n"
50
+ skills_content = self._load_skills(recipe)
51
+ system_prompt += skills_content
52
+
53
+ system_prompt += "\n\n## Execution Rules\n"
54
+ system_prompt += "- Follow the skill instructions strictly.\n"
55
+ system_prompt += "- If user query doesn\'t match any skill, fall back to general assistance.\n"
56
+
57
+ # Construct Coze DSL
58
+ bot_config = {
59
+ "bot_info": {
60
+ "name": recipe.name,
61
+ "description": recipe.notes or f"Agent for {recipe.name}",
62
+ "prompt_info": {
63
+ "prompt": system_prompt
64
+ },
65
+ "onboarding_info": {
66
+ "prologue": f"你好,我是{recipe.name}助手。请告诉我你需要什么帮助?"
67
+ }
68
+ },
69
+ "model_info": {
70
+ "model_name": "coze-pro"
71
+ },
72
+ "metadata": {
73
+ "source": "agent-assembler",
74
+ "recipe_name": recipe.name,
75
+ "trigger_keywords": recipe.trigger_keywords,
76
+ "skills_count": len(recipe.skills)
77
+ }
78
+ }
79
+
80
+ return bot_config
81
+
82
+ def validate(self, recipe: Recipe) -> List[str]:
83
+ """Validate recipe against Coze platform constraints."""
84
+ errors = []
85
+ if len(recipe.name) > 50:
86
+ errors.append(f"Bot name \'{recipe.name}\' exceeds 50 characters limit.")
87
+ if not recipe.name:
88
+ errors.append("Bot name cannot be empty.")
89
+ if len(recipe.notes) > 500:
90
+ errors.append("Bot description exceeds 500 characters limit.")
91
+ return errors
@@ -0,0 +1,98 @@
1
+ from typing import Any, Dict, List, Optional
2
+ from ..recipe import Recipe
3
+ from .base import BaseAdapter
4
+ import os
5
+
6
+
7
+ class QianwenAdapter(BaseAdapter):
8
+ """Map Agent Assembler Recipe to Qianwen (Tongyi) Agent Format.
9
+
10
+ Qianwen agents (百炼/通义) require:
11
+ - name: Agent name
12
+ - description: Brief description
13
+ - system_prompt: Core instructions
14
+ - model: LLM model selection
15
+
16
+ This adapter generates Chinese-optimized prompts.
17
+ """
18
+
19
+ PLATFORM_NAME = "Qianwen"
20
+
21
+ def __init__(self, skills_dir: Optional[str] = None):
22
+ """Initialize with optional skills directory for JIT loading."""
23
+ self.skills_dir = skills_dir
24
+
25
+ def _load_skills(self, recipe: Recipe) -> str:
26
+ """Load skill contents and format as prompt section."""
27
+ if not self.skills_dir:
28
+ return "(未配置技能目录)"
29
+
30
+ sections = []
31
+ for skill_name in recipe.skills:
32
+ skill_path = os.path.join(self.skills_dir, skill_name, "SKILL.md")
33
+ if os.path.exists(skill_path):
34
+ with open(skill_path, "r", encoding="utf-8") as f:
35
+ sections.append(f"### 技能: {skill_name}\n{f.read()}")
36
+ else:
37
+ sections.append(f"### 技能: {skill_name}\n[警告: 技能文件未找到]")
38
+
39
+ return "\n\n".join(sections)
40
+
41
+ def export(self, recipe: Recipe) -> Dict[str, Any]:
42
+ """Export recipe as Qianwen-compatible agent configuration."""
43
+ # Build system prompt in Chinese
44
+ system_prompt = f"# 角色设定\n你是一个专业的 **{recipe.name}** 助手。\n\n"
45
+
46
+ if recipe.notes:
47
+ system_prompt += f"## 背景信息\n{recipe.notes}\n\n"
48
+
49
+ system_prompt += "## 可用技能与指令\n"
50
+ skills_content = self._load_skills(recipe)
51
+ system_prompt += skills_content
52
+
53
+ system_prompt += "\n\n## 执行规则\n"
54
+ system_prompt += "- 严格按照技能指令执行任务\n"
55
+ system_prompt += "- 如果用户请求不匹配任何技能,提供通用帮助\n"
56
+ system_prompt += "- 保持回答简洁、专业、准确\n"
57
+
58
+ # Construct Qianwen DSL
59
+ agent_config = {
60
+ "name": recipe.name,
61
+ "description": recipe.notes or f"智能体: {recipe.name}",
62
+ "system_prompt": system_prompt,
63
+ "welcome_message": f"你好,我是{recipe.name}助手,请问有什么可以帮您?",
64
+ "model": "qwen-max",
65
+ "parameters": {
66
+ "temperature": 0.7,
67
+ "top_p": 0.8,
68
+ "max_tokens": 2000
69
+ },
70
+ "metadata": {
71
+ "source": "agent-assembler",
72
+ "recipe_name": recipe.name,
73
+ "trigger_keywords": recipe.trigger_keywords,
74
+ "skills_count": len(recipe.skills),
75
+ "routing": recipe.routing
76
+ }
77
+ }
78
+
79
+ return agent_config
80
+
81
+ def validate(self, recipe: Recipe) -> List[str]:
82
+ """Validate recipe against Qianwen platform constraints."""
83
+ errors = []
84
+ if len(recipe.name) > 30:
85
+ errors.append(f"智能体名称 \'{recipe.name}\' 超过 30 字符限制。")
86
+ if not recipe.name:
87
+ errors.append("智能体名称不能为空。")
88
+ # Check for valid routing if specified
89
+ valid_routings = [
90
+ None, "engineering-stage-agent", "finance-agent",
91
+ "operations-venue-agent", "project-pmo-agent",
92
+ "digital-tech-agent", "planning-agent",
93
+ "marketing-promotion-agent", "legal-agent",
94
+ "agriculture-agent", "guandan-agent", "gr-agent", "media-agent"
95
+ ]
96
+ if recipe.routing and recipe.routing not in valid_routings:
97
+ errors.append(f"路由目标 \'{recipe.routing}\' 不在有效 Agent 列表中。")
98
+ return errors
@@ -0,0 +1,85 @@
1
+
2
+ import os
3
+ import json
4
+ import re
5
+ from typing import List, Optional, Dict
6
+ from .recipe import Recipe
7
+
8
+ class Assembler:
9
+ """The core engine of Agent Assembler."""
10
+
11
+ def __init__(self, recipes_dir: str, skills_dir: str):
12
+ self.recipes_dir = os.path.abspath(recipes_dir)
13
+ self.skills_dir = os.path.abspath(skills_dir)
14
+ self.recipes: List[Recipe] = []
15
+
16
+ self._load_recipes()
17
+
18
+ def _load_recipes(self):
19
+ """Load all recipes from the configured directory."""
20
+ if not os.path.exists(self.recipes_dir):
21
+ raise FileNotFoundError(f"Recipes directory not found: {self.recipes_dir}")
22
+
23
+ for root, _, files in os.walk(self.recipes_dir):
24
+ for file in files:
25
+ if file.endswith('.json'):
26
+ path = os.path.join(root, file)
27
+ try:
28
+ recipe = Recipe.from_json(path)
29
+ # Pre-load skills? No, JIT load them when matched to save time/memory.
30
+ # But for SDK v1, maybe eager load is fine for small datasets.
31
+ # Let's do lazy loading in assemble() for better performance.
32
+ self.recipes.append(recipe)
33
+ except Exception as e:
34
+ print(f"Error loading recipe {path}: {e}")
35
+
36
+ def match_recipe(self, query: str) -> Optional[Recipe]:
37
+ """Find the best matching recipe for a user query."""
38
+ query_lower = query.lower()
39
+ matches = []
40
+
41
+ for recipe in self.recipes:
42
+ for keyword in recipe.trigger_keywords:
43
+ if keyword.lower() in query_lower:
44
+ matches.append(recipe)
45
+ break # Found a match for this recipe
46
+
47
+ if not matches:
48
+ return None
49
+
50
+ # Return the first match (can be improved with scoring later)
51
+ return matches[0]
52
+
53
+ def assemble(self, query: str) -> Dict:
54
+ """Perform JIT assembly."""
55
+ recipe = self.match_recipe(query)
56
+
57
+ if not recipe:
58
+ return {
59
+ "status": "fallback",
60
+ "message": "No matching recipe found.",
61
+ "system_prompt": query # Pass through
62
+ }
63
+
64
+ # Load skills JIT
65
+ recipe.load_skills(self.skills_dir)
66
+
67
+ # Construct Prompt
68
+ system_prompt = f"# Role\nYou are an AI assistant executing the task: {recipe.name}\n\n"
69
+
70
+ if recipe.notes:
71
+ system_prompt += f"## Context\n{recipe.notes}\n\n"
72
+
73
+ system_prompt += "## Skills & Rules\n"
74
+
75
+ for skill_ref in recipe.skill_refs:
76
+ system_prompt += f"### Skill: {skill_ref.name}\n{skill_ref.content}\n\n"
77
+
78
+ system_prompt += f"## User Query\n{query}"
79
+
80
+ return {
81
+ "status": "success",
82
+ "recipe": recipe.name,
83
+ "skills_loaded": [s.name for s in recipe.skill_refs],
84
+ "system_prompt": system_prompt
85
+ }
@@ -0,0 +1,3 @@
1
+
2
+ from .coze_api import CozeApiClient
3
+ from .qianwen_api import QianwenApiClient
@@ -0,0 +1,95 @@
1
+
2
+ import requests
3
+ import json
4
+ from typing import Dict, Any, List, Optional
5
+
6
+ class CozeApiClient:
7
+ # China Domestic API
8
+ API_BASE = "https://api.coze.cn/v1"
9
+
10
+ def __init__(self, token: str):
11
+ self.headers = {
12
+ "Authorization": f"Bearer {token}",
13
+ "Content-Type": "application/json"
14
+ }
15
+
16
+ def create_bot(self, name: str, description: str, prompt: str, space_id: str) -> Optional[str]:
17
+ """
18
+ Create a bot on Coze (v1 API).
19
+ Returns bot_id if success.
20
+ """
21
+ url = f"{self.API_BASE}/bot/create"
22
+ payload = {
23
+ "space_id": space_id,
24
+ "name": name,
25
+ "description": description,
26
+ "prompt_info": {
27
+ "prompt": prompt
28
+ }
29
+ }
30
+
31
+ try:
32
+ resp = requests.post(url, headers=self.headers, json=payload)
33
+ data = resp.json()
34
+ if data.get("code") == 0:
35
+ bot_id = data.get("data", {}).get("bot_id")
36
+ print(f"Bot created: {bot_id}")
37
+ return bot_id
38
+ else:
39
+ error_msg = data.get("msg", "Unknown error")
40
+ print(f"Coze API Error: {error_msg}")
41
+ return None
42
+ except Exception as e:
43
+ print(f"Request Error: {e}")
44
+ return None
45
+
46
+ def install_connector(self, connector_id: str, workspace_id: str) -> bool:
47
+ """
48
+ Add a publishing channel (Connector) to the workspace.
49
+ e.g. connector_id for Douyin, Feishu, etc.
50
+ """
51
+ url = f"{self.API_BASE}/connectors/{connector_id}/install"
52
+ payload = {
53
+ "workspace_id": workspace_id
54
+ }
55
+
56
+ try:
57
+ resp = requests.post(url, headers=self.headers, json=payload)
58
+ data = resp.json()
59
+ if data.get("code") == 0:
60
+ print(f"Connector {connector_id} installed to workspace {workspace_id}")
61
+ return True
62
+ else:
63
+ print(f"Install Connector Error: {data.get('msg')}")
64
+ return False
65
+ except Exception as e:
66
+ print(f"Request Error: {e}")
67
+ return False
68
+
69
+ def publish_bot(self, bot_id: str, connector_ids: List[str] = None) -> bool:
70
+ """
71
+ Publish the bot to specified channels.
72
+ Default connector_ids: ['1024'] (API/SDK)
73
+ """
74
+ if connector_ids is None:
75
+ connector_ids = ["1024"]
76
+
77
+ url = f"{self.API_BASE}/bot/publish"
78
+ payload = {
79
+ "bot_id": bot_id,
80
+ "connector_ids": connector_ids
81
+ }
82
+
83
+ try:
84
+ resp = requests.post(url, headers=self.headers, json=payload)
85
+ data = resp.json()
86
+ if data.get("code") == 0:
87
+ print(f"Bot {bot_id} published to {connector_ids}")
88
+ return True
89
+ else:
90
+ print(f"Publish Bot Error: {data.get('msg')}")
91
+ return False
92
+ except Exception as e:
93
+ print(f"Publish Error: {e}")
94
+ return False
95
+
@@ -0,0 +1,58 @@
1
+
2
+ import requests
3
+ import json
4
+ from typing import Dict, Any, Optional
5
+
6
+ class QianwenApiClient:
7
+ # This is a placeholder for the specific Bailian/Model Studio Agent API
8
+ # As of now, Bailian Agent API might be complex.
9
+ # We'll mock the structure based on common patterns or use a documented endpoint if available.
10
+ # Usually involves: Create Application -> Publish.
11
+
12
+ API_BASE = "https://dashscope.aliyuncs.com/api/v1/apps" # Example endpoint
13
+
14
+ def __init__(self, api_key: str):
15
+ self.headers = {
16
+ "Authorization": f"Bearer {api_key}",
17
+ "Content-Type": "application/json",
18
+ "X-DashScope-SSE": "disable" # Disable streaming for simple calls
19
+ }
20
+
21
+ def create_agent(self, name: str, description: str, prompt: str) -> Optional[str]:
22
+ """
23
+ Create an Agent/App on Bailian.
24
+ Returns app_id.
25
+ """
26
+ # Note: Real endpoint might differ. Using a representative structure.
27
+ url = f"{self.API_BASE}"
28
+ payload = {
29
+ "name": name,
30
+ "description": description,
31
+ "prompt": prompt,
32
+ "model": "qwen-max" # Default model
33
+ }
34
+
35
+ try:
36
+ # POST to create
37
+ # If this endpoint is not public for agents, this will fail gracefully
38
+ # For now, we assume a standard pattern
39
+ resp = requests.post(url, headers=self.headers, json=payload)
40
+ data = resp.json()
41
+
42
+ # Check for success (structure varies)
43
+ if data.get("code") == 200 or "id" in data:
44
+ return data.get("id") or data.get("output", {}).get("app_id")
45
+ else:
46
+ print(f"Qianwen API Error: {data}")
47
+ return None
48
+ except Exception as e:
49
+ print(f"Request Error: {e}")
50
+ return None
51
+
52
+ def publish_agent(self, app_id: str) -> bool:
53
+ """
54
+ Publish the agent.
55
+ """
56
+ # Usually requires a separate publish call or versioning.
57
+ print(f"Publishing {app_id}...")
58
+ return True # Placeholder
@@ -0,0 +1,66 @@
1
+
2
+ import json
3
+ import os
4
+ from dataclasses import dataclass, field
5
+ from typing import List, Optional, Dict, Any
6
+ from pathlib import Path
7
+
8
+ @dataclass
9
+ class SkillRef:
10
+ name: str
11
+ content: str = ""
12
+ path: str = ""
13
+ loaded: bool = False
14
+
15
+ def load_content(self, root_dir: str):
16
+ """Load the actual skill content from file system."""
17
+ # Try to find the skill file
18
+ # Structure: {root_dir}/{skill_name}/SKILL.md
19
+ potential_path = os.path.join(root_dir, self.name, "SKILL.md")
20
+ if os.path.exists(potential_path):
21
+ with open(potential_path, "r", encoding="utf-8") as f:
22
+ self.content = f.read()
23
+ self.path = potential_path
24
+ self.loaded = True
25
+ return True
26
+ return False
27
+
28
+ @dataclass
29
+ class Recipe:
30
+ """Recipe maps user intent to specific skills."""
31
+ name: str
32
+ trigger_keywords: List[str]
33
+ skills: List[str] = field(default_factory=list) # Skill names
34
+ notes: str = ""
35
+ routing: Optional[str] = None
36
+ _skill_refs: List[SkillRef] = field(default_factory=list, init=False)
37
+
38
+ @property
39
+ def skill_refs(self) -> List[SkillRef]:
40
+ return self._skill_refs
41
+
42
+ def load_skills(self, skills_dir: str):
43
+ """Load all skills defined in this recipe."""
44
+ self._skill_refs = []
45
+ for skill_name in self.skills:
46
+ ref = SkillRef(name=skill_name)
47
+ if ref.load_content(skills_dir):
48
+ self._skill_refs.append(ref)
49
+ else:
50
+ print(f"[Warning] Skill not found: {skill_name} in {skills_dir}")
51
+
52
+ @classmethod
53
+ def from_dict(cls, data: Dict[str, Any], source_file: str = ""):
54
+ return cls(
55
+ name=data.get("name", Path(source_file).stem),
56
+ trigger_keywords=data.get("trigger_keywords", []),
57
+ skills=data.get("skills", []),
58
+ notes=data.get("notes", ""),
59
+ routing=data.get("routing")
60
+ )
61
+
62
+ @classmethod
63
+ def from_json(cls, path: str):
64
+ with open(path, 'r', encoding="utf-8") as f:
65
+ data = json.load(f)
66
+ return cls.from_dict(data, path)
@@ -0,0 +1,51 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-assembler
3
+ Version: 0.2.0
4
+ Summary: Deterministic Context Assembly for AI Agents.
5
+ License: MIT
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: streamlit
10
+ Requires-Dist: requests
11
+ Requires-Dist: pandas
12
+ Dynamic: license-file
13
+
14
+ # Agent Assembler 🧩
15
+
16
+ > **Deterministic Context Assembly for AI Agents.**
17
+ > The Engine for the Multi-Agent Distribution Network.
18
+
19
+ ⚠️ **Status**: Currently in active development (Phase 1: SDK Decoupling). Please clone from GitHub for now. `pip install` package coming soon in Phase 2.
20
+
21
+ ## 1. Vision
22
+ **From JIT Engine to Agent Factory & Distribution Network.**
23
+ Agent Assembler is no longer just a script; it is the **core engine** that powers multi-agent systems across platforms (Qianwen, Coze, WeChat, etc.).
24
+
25
+ ## 2. Core Architecture
26
+ - **Recipe-First**: Intent matching pre-defined JSON recipes.
27
+ - **Atomic Skills**: <4KB focused skill modules.
28
+ - **JIT Assembly**: Assemble only what is needed, when it is needed.
29
+ - **Multi-Platform Adapters**: Deploy to Qianwen, Coze, Baidu, and more with one click.
30
+
31
+ ## 3. Roadmap
32
+ | Phase | Goal | Status |
33
+ |-------|------|--------|
34
+ | **P0** | Core Stabilization & Validation | ✅ Done |
35
+ | **P1** | **SDK Decoupling & Standardization** | 🚧 Active |
36
+ | **P2** | Multi-Platform Adapters (Coze/Qianwen) | ⬜ Planned |
37
+ | **P3** | SaaS Dashboard & No-Code Builder | ⬜ Planned |
38
+
39
+ ## 4. Installation
40
+ ```bash
41
+ pip install agent-assembler
42
+ ```
43
+
44
+ ## 5. Quick Start
45
+ ```python
46
+ from agent_assembler import Assembler
47
+
48
+ assembler = Assembler(recipes_dir="./recipes", skills_dir="./skills")
49
+ result = assembler.assemble("Analyze this excel file")
50
+ print(result['system_prompt'])
51
+ ```
@@ -0,0 +1,15 @@
1
+ agent_assembler/__init__.py,sha256=o2J8n-bbNtiwI2OBBfFFFtRgwaCb5t5VZo2oopJ9t_8,124
2
+ agent_assembler/assembler.py,sha256=SsCVYJzDC2EzoAm5qpdZXezJYon3mWsj39sS6Lotw1A,3073
3
+ agent_assembler/recipe.py,sha256=f1OSFN9AyfCnMSbZbAcMx0Yk1oyN92jF6atjzVVnh20,2130
4
+ agent_assembler/adapters/__init__.py,sha256=mXC0Tte8yCjm7kefiCw-sRfz-hiDIDKTKSreAEEyPb0,156
5
+ agent_assembler/adapters/base.py,sha256=VMXE4PvB25RysuaHU80aSs5TQ09DDHYI_jGXPvwFCJw,649
6
+ agent_assembler/adapters/coze.py,sha256=zlAVQrxrQiUCbvyVeNm_cDkr4JgEJqKHdnfYcfghApA,3378
7
+ agent_assembler/adapters/qianwen.py,sha256=B14UNGiK7Nsat68Pjtt509pjzbYIZd2S3JhFOdCrSP4,3862
8
+ agent_assembler/deploy/__init__.py,sha256=vCl4DSaIIeWFb76dhlhJaf6_KzKPY_AMwvstil6lhXQ,79
9
+ agent_assembler/deploy/coze_api.py,sha256=JcyDhF5T46DDrmOpCdOp01LVQ95LS1AwyfR5MyR7LIg,3096
10
+ agent_assembler/deploy/qianwen_api.py,sha256=3I1NS44bsQa9MOAIsLz8HhR1WAcZ39KwkOqYnEL1Sl4,2103
11
+ agent_assembler-0.2.0.dist-info/licenses/LICENSE,sha256=wWbdNra8DIz9HRQ77dt2fCUWx51WjfpYU-feI4YJ9g0,1070
12
+ agent_assembler-0.2.0.dist-info/METADATA,sha256=BkLqgjnsFm8YoOhAonWlpO0nRI7LV7_ziU8hmRyS1vs,1715
13
+ agent_assembler-0.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
14
+ agent_assembler-0.2.0.dist-info/top_level.txt,sha256=l8ktHSG__P_oBMzzxqCFUNRZ8Jcf-t44zuX-GgXmfZM,16
15
+ agent_assembler-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,22 @@
1
+
2
+ MIT License
3
+
4
+ Copyright (c) 2026 Hermes Agent
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ agent_assembler