From e9c97d7c2670212e0df2bff09a2fdf562af5f87f Mon Sep 17 00:00:00 2001 From: sawradip Date: Sat, 26 Jul 2025 04:04:29 +0800 Subject: [PATCH 01/16] feat: n8n support wip --- runagent/sdk/server/framework/n8n.py | 195 ++++++++++++++++++ runagent/utils/schema.py | 1 + templates/n8n/default/requirements.txt | 1 + templates/n8n/default/runagent.config.json | 25 +++ templates/n8n/default/simple_assistant.py | 24 +++ templates/n8n/email_agent/email_agent.py | 54 +++++ templates/n8n/email_agent/requirements.txt | 1 + .../n8n/email_agent/runagent.config.json | 30 +++ 8 files changed, 331 insertions(+) create mode 100644 runagent/sdk/server/framework/n8n.py create mode 100644 templates/n8n/default/requirements.txt create mode 100644 templates/n8n/default/runagent.config.json create mode 100644 templates/n8n/default/simple_assistant.py create mode 100644 templates/n8n/email_agent/email_agent.py create mode 100644 templates/n8n/email_agent/requirements.txt create mode 100644 templates/n8n/email_agent/runagent.config.json diff --git a/runagent/sdk/server/framework/n8n.py b/runagent/sdk/server/framework/n8n.py new file mode 100644 index 0000000..55c697d --- /dev/null +++ b/runagent/sdk/server/framework/n8n.py @@ -0,0 +1,195 @@ +import asyncio +import httpx +import inspect +from pathlib import Path +from typing import Dict, List +from rich.console import Console +from runagent.utils.imports import PackageImporter +from runagent.utils.schema import EntryPoint, RunAgentConfig +from runagent.utils.serializer import CoreSerializer +from runagent.utils.response import extract_jsonpath, to_dict + +console = Console() + + +class N8nExecutor: + + rerserved_tags = list() + + def __init__(self, agent_dir: Path): + self.agent_dir = agent_dir + + def get_runner(self, entrypoint: EntryPoint): + """ + Always returns an async function, regardless of whether the + underlying entrypoint is sync or async. + """ + resolved_entrypoint = self._entrypoint_resolver( + entrypoint_filepath=self.agent_dir / entrypoint.file, + entrypoint_module=entrypoint.module, + ) + + async def normalized_runner(*input_args, **input_kwargs): + console.print(f"πŸ” [cyan]Entrypoint:[/cyan] {entrypoint.tag}") + console.print(f"πŸ” [cyan]Input data:[/cyan] *{input_args}, **{input_kwargs}") + + # Check for invalid argument combinations + if len(input_args) > 1: + raise ValueError("Too many positional arguments. Expected at most 1.") + + if input_args and input_kwargs: + raise ValueError("Cannot specify both positional and keyword arguments.") + + if len(input_kwargs) > 1 or (input_kwargs and 'payload' not in input_kwargs): + raise ValueError("Only 'payload' keyword argument is allowed.") + + # Extract payload + if input_args: + # normalized_runner({...}) + payload = input_args[0] + elif 'payload' in input_kwargs: + # normalized_runner(payload={...}) + payload = input_kwargs['payload'] + else: + # normalized_runner() + payload = None + + async with httpx.AsyncClient() as client: + if entrypoint.method == "post": + # POST request with JSON payload + response = await client.post( + entrypoint.webhook_url, + json=payload, + headers={"Content-Type": "application/json"} + ) + elif entrypoint.method == "get": + # GET request when no payload + response = await client.get(entrypoint.webhook_url) + + response.raise_for_status() # Raise exception for HTTP errors + result = response.json() + + if entrypoint.extractor: + result = extract_jsonpath(result, entrypoint.extractor) + + return result + + + + + # async def normalized_runner(*input_args, **input_kwargs): + # console.print(f"πŸ” [cyan]Entrypoint:[/cyan] {entrypoint.tag}") + # console.print(f"πŸ” [cyan]Input data:[/cyan] *{input_args}, **{input_kwargs}") + + # if inspect.iscoroutinefunction(resolved_entrypoint): + # print("resolved async", (entrypoint.tag, entrypoint.module, resolved_entrypoint)) + # result = await resolved_entrypoint(*input_args, **input_kwargs) + # else: + # print("resolved sync (wrapped in async)", (entrypoint.tag, entrypoint.module, resolved_entrypoint)) + # # Run sync function in executor to avoid blocking + # result = await asyncio.get_event_loop().run_in_executor( + # None, lambda: resolved_entrypoint(*input_args, **input_kwargs) + # ) + + # if entrypoint.extractor: + # result = extract_jsonpath(result, entrypoint.extractor) + + # return result + + # return normalized_runner + + # def get_stream_runner(self, entrypoint: EntryPoint): + # """ + # Always returns an async generator, regardless of whether the + # underlying entrypoint is sync or async. + # """ + # resolved_entrypoint = self._entrypoint_resolver( + # entrypoint_filepath=self.agent_dir / entrypoint.file, + # entrypoint_module=entrypoint.module, + # ) + + # async def normalized_stream_runner(*input_args, **input_kwargs): + # console.print(f"πŸ” [cyan]Entrypoint:[/cyan] {entrypoint.tag}") + # console.print(f"πŸ” [cyan]Input data:[/cyan] *{input_args}, **{input_kwargs}") + + # if inspect.isasyncgenfunction(resolved_entrypoint): + # # Native async generator + # print("resolved async generator", (entrypoint.tag, entrypoint.module, resolved_entrypoint)) + # async for chunk in resolved_entrypoint(*input_args, **input_kwargs): + # if entrypoint.extractor: + # chunk = extract_jsonpath(chunk, entrypoint.extractor) + # yield chunk + # await asyncio.sleep(0) + + # elif inspect.iscoroutinefunction(resolved_entrypoint): + # # Async function that returns iterable + # print("resolved async function (returns iterable)", (entrypoint.tag, entrypoint.module, resolved_entrypoint)) + # result = await resolved_entrypoint(*input_args, **input_kwargs) + # for chunk in result: + # if entrypoint.extractor: + # chunk = extract_jsonpath(chunk, entrypoint.extractor) + # yield chunk + # await asyncio.sleep(0) + + # elif inspect.isgeneratorfunction(resolved_entrypoint): + # # Sync generator - consume in executor + # print("resolved sync generator (wrapped in async)", (entrypoint.tag, entrypoint.module, resolved_entrypoint)) + # generator = await asyncio.get_event_loop().run_in_executor( + # None, lambda: resolved_entrypoint(*input_args, **input_kwargs) + # ) + # for chunk in generator: + # if entrypoint.extractor: + # chunk = extract_jsonpath(chunk, entrypoint.extractor) + # yield chunk + # await asyncio.sleep(0) + + # else: + # # Sync function that returns iterable + # print("resolved sync function (returns iterable, wrapped in async)", (entrypoint.tag, entrypoint.module, resolved_entrypoint)) + # result = await asyncio.get_event_loop().run_in_executor( + # None, lambda: resolved_entrypoint(*input_args, **input_kwargs) + # ) + # for chunk in result: + # if entrypoint.extractor: + # chunk = extract_jsonpath(chunk, entrypoint.extractor) + # yield chunk + # await asyncio.sleep(0) + + # return normalized_stream_runner + + # def _entrypoint_resolver(self, entrypoint_filepath: Path, entrypoint_module: str): + # print(f"DEBUG: Resolving entrypoint - filepath: {entrypoint_filepath}, module: {entrypoint_module}") + # primary_module, secondary_attr = ( + # entrypoint_module.split(".", 1) + [""] + # )[:2] + # print(f"DEBUG: Split module - primary: {primary_module}, secondary: {secondary_attr}") + + # resolved_module = self.importer.resolve_import( + # entrypoint_filepath, primary_module + # ) + # print(f"DEBUG: Resolved primary module: {resolved_module}") + + # if secondary_attr: + # attrs = secondary_attr.split('.') + # print(f"DEBUG: Secondary attributes to resolve: {attrs}") + # for attr in attrs: + # resolved_module = getattr(resolved_module, attr) + # print(f"DEBUG: Resolved attribute {attr}: {resolved_module}") + + # print(f"DEBUG: Final resolved module: {resolved_module}") + # print("Resolving", (entrypoint_module, resolved_module)) + # return resolved_module + + # def _get_function_type(self, func): + # """ + # Determine the type of function for debugging purposes. + # Returns: str describing the function type + # """ + # if inspect.isasyncgenfunction(func): + # return "async_generator" + # elif inspect.iscoroutinefunction(func): + # return "async_function" + # elif inspect.isgeneratorfunction(func): + # return "sync_generator" + # else: + # return "sync_function" \ No newline at end of file diff --git a/runagent/utils/schema.py b/runagent/utils/schema.py index cb73acc..f108a91 100644 --- a/runagent/utils/schema.py +++ b/runagent/utils/schema.py @@ -25,6 +25,7 @@ class EntryPoint(BaseModel): ) + class AgentArchitecture(BaseModel): """Agent architecture configuration""" diff --git a/templates/n8n/default/requirements.txt b/templates/n8n/default/requirements.txt new file mode 100644 index 0000000..ec838c5 --- /dev/null +++ b/templates/n8n/default/requirements.txt @@ -0,0 +1 @@ +openai diff --git a/templates/n8n/default/runagent.config.json b/templates/n8n/default/runagent.config.json new file mode 100644 index 0000000..248067b --- /dev/null +++ b/templates/n8n/default/runagent.config.json @@ -0,0 +1,25 @@ +{ + "agent_name": "Demo Agent", + "description": "A simple placeholder agent", + "framework": "n8n", + "template": "default", + "version": "1.0.0", + "created_at": "2025-06-25 13:42:03", + "template_source": { + "repo_url": "https://github.com/runagent-dev/runagent.git", + "path": "templates/default", + "author": "sawradip" + }, + "agent_architecture": { + "entrypoints": [ + { + "webhook_url": "https://taniatinni.app.n8n.cloud/webhook/recipe-nutrition", + "method": "post", + "tag": "food_doctor" + } + ] + }, + "env_vars": { + "OPENAI_API_KEY": "sk-proj-1234567890" + } +} \ No newline at end of file diff --git a/templates/n8n/default/simple_assistant.py b/templates/n8n/default/simple_assistant.py new file mode 100644 index 0000000..6669cb3 --- /dev/null +++ b/templates/n8n/default/simple_assistant.py @@ -0,0 +1,24 @@ +from openai import OpenAI + + +def get_response(user_msg: str) -> str: + client = OpenAI() + return client.chat.completions.create( + model="gpt-4o-mini", + messages=[ + {"role": "developer", "content": "You are a helpful assistant."}, + {"role": "user", "content": user_msg} + ] + ) + + +def get_response_stream(user_msg: str) -> str: + client = OpenAI() + return client.chat.completions.create( + model="gpt-4o-mini", + stream=True, + messages=[ + {"role": "developer", "content": "You are a helpful assistant."}, + {"role": "user", "content": user_msg} + ] + ) diff --git a/templates/n8n/email_agent/email_agent.py b/templates/n8n/email_agent/email_agent.py new file mode 100644 index 0000000..c157170 --- /dev/null +++ b/templates/n8n/email_agent/email_agent.py @@ -0,0 +1,54 @@ +from openai import OpenAI +from openai.types.responses import ResponseTextDeltaEvent +client = OpenAI() + + +def get_prompt(sender_name: str, recipient_name: str, subject: str) -> str: + return ( + f"Write a professional email with the following details:\n" + f"- Sender: {sender_name}\n" + f"- Recipient: {recipient_name}\n" + f"- Subject: {subject}\n\n" + f"The email should be concise, polite, and use natural language. Include a greeting, body, and closing." + ) + + +def generate_email(sender_name: str, recipient_name: str, subject: str) -> str: + prompt = get_prompt(sender_name, recipient_name, subject) + + response = client.responses.create( + model="gpt-4", + input=prompt + ) + + email_text = response.output_text.strip() + return email_text + + +def generate_email_sream(sender_name: str, recipient_name: str, subject: str) -> str: + prompt = get_prompt(sender_name, recipient_name, subject) + + stream_response = client.responses.create( + model="gpt-4", + input=prompt, + stream=True + ) + + for chunk in stream_response: + if isinstance(chunk, ResponseTextDeltaEvent): + yield chunk.delta + + +# Example usage +if __name__ == "__main__": + sender = "Alice Johnson" + recipient = "Mr. Daniel Smith" + subject = "Request for Meeting Next Week" + + email = generate_email(sender, recipient, subject) + print("\n--- Generated Email ---\n") + print(email) + + # print("\n--- Generated Email Stream---\n") + # for chunk in generate_email_sream(sender, recipient, subject): + # print(chunk) diff --git a/templates/n8n/email_agent/requirements.txt b/templates/n8n/email_agent/requirements.txt new file mode 100644 index 0000000..ec838c5 --- /dev/null +++ b/templates/n8n/email_agent/requirements.txt @@ -0,0 +1 @@ +openai diff --git a/templates/n8n/email_agent/runagent.config.json b/templates/n8n/email_agent/runagent.config.json new file mode 100644 index 0000000..5b0a8b8 --- /dev/null +++ b/templates/n8n/email_agent/runagent.config.json @@ -0,0 +1,30 @@ +{ + "agent_name": "Demo Agent", + "description": "A simple placeholder agent", + "framework": "openai", + "template": "default", + "version": "1.0.0", + "created_at": "2025-06-25 13:42:03", + "template_source": { + "repo_url": "https://github.com/runagent-dev/runagent.git", + "path": "templates/default", + "author": "sawradip" + }, + "agent_architecture": { + "entrypoints": [ + { + "file": "email_agent.py", + "module": "generate_email", + "tag": "generic_email" + }, + { + "file": "email_agent.py", + "module": "generate_email_sream", + "tag": "email_stream" + } + ] + }, + "env_vars": { + "OPENAI_API_KEY": "sk-proj-1234567890" + } +} \ No newline at end of file From 81739503fbe437204ad39e458529ac88bfe1a650 Mon Sep 17 00:00:00 2001 From: sawradip Date: Sun, 27 Jul 2025 11:31:21 +0600 Subject: [PATCH 02/16] feat: added n8n executor & webhook entrypoint --- runagent/cli/commands.py | 8 +- runagent/sdk/server/framework/__init__.py | 10 +- runagent/sdk/server/framework/generic.py | 6 +- runagent/sdk/server/framework/n8n.py | 144 +++------------------- runagent/utils/schema.py | 63 ++++++---- 5 files changed, 68 insertions(+), 163 deletions(-) diff --git a/runagent/cli/commands.py b/runagent/cli/commands.py index 06f7973..ed860e0 100644 --- a/runagent/cli/commands.py +++ b/runagent/cli/commands.py @@ -720,9 +720,9 @@ def deploy(folder, agent_id, local, framework, config): @click.option("--debug", is_flag=True, help="Run server in debug mode") @click.option("--replace", help="Replace existing agent with this agent ID") @click.option("--no-animation", is_flag=True, help="Skip startup animation") -@click.option("--animation-style", - type=click.Choice(["field", "ascii", "minimal", "quick"]), - default="field", +@click.option("--animation-style", + type=click.Choice(["field", "ascii", "minimal", "quick"]), + default="field", help="Animation style") @click.argument( "path", @@ -900,7 +900,7 @@ def run(ctx, agent_id, host, port, input_file, local, tag, timeout): raise click.UsageError( "Must specify either --agent-id or both --host and --port." ) - + # If using host/port, both must be provided if host_port_provided and (host is None or port is None): raise click.UsageError( diff --git a/runagent/sdk/server/framework/__init__.py b/runagent/sdk/server/framework/__init__.py index 0f4751e..b909474 100644 --- a/runagent/sdk/server/framework/__init__.py +++ b/runagent/sdk/server/framework/__init__.py @@ -1,6 +1,6 @@ from pathlib import Path -from typing import Dict - +# from typing import Dict +import typing as t from runagent.sdk.server.framework.langgraph import LangGraphExecutor from runagent.sdk.server.framework.langchain import LangChainExecutor from runagent.sdk.server.framework.openai import OpenAIExecutor @@ -10,11 +10,12 @@ from runagent.sdk.server.framework.autogen import AutogenExecutor from runagent.sdk.server.framework.crewai import CrewAIExecutor from runagent.sdk.server.framework.ag2 import AG2Executor -from runagent.utils.schema import EntryPoint +from runagent.sdk.server.framework.n8n import N8NExecutor +from runagent.utils.schema import PythonicEntryPoint, WebHookEntryPoint def get_executor( - agent_dir: Path, framework: str, agent_entrypoints: Dict[str, EntryPoint] + agent_dir: Path, framework: str, agent_entrypoints: t.Dict[str, t.Union[PythonicEntryPoint, WebHookEntryPoint]] ): executor_dict = { "default": GenericExecutor, @@ -27,6 +28,7 @@ def get_executor( "langchain": GenericExecutor, "letta": GenericExecutor, "llamaindex": LlamaIndexExecutor, + "n8n": N8NExecutor } framework_executor = executor_dict.get(framework) if framework_executor is None: diff --git a/runagent/sdk/server/framework/generic.py b/runagent/sdk/server/framework/generic.py index 5cd600e..a9e9c3a 100644 --- a/runagent/sdk/server/framework/generic.py +++ b/runagent/sdk/server/framework/generic.py @@ -4,7 +4,7 @@ from typing import Dict, List from rich.console import Console from runagent.utils.imports import PackageImporter -from runagent.utils.schema import EntryPoint, RunAgentConfig +from runagent.utils.schema import PythonicEntryPoint, RunAgentConfig from runagent.utils.serializer import CoreSerializer from runagent.utils.response import extract_jsonpath, to_dict @@ -19,7 +19,7 @@ def __init__(self, agent_dir: Path): self.agent_dir = agent_dir self.importer = PackageImporter(verbose=False) - def get_runner(self, entrypoint: EntryPoint): + def get_runner(self, entrypoint: PythonicEntryPoint): """ Always returns an async function, regardless of whether the underlying entrypoint is sync or async. @@ -50,7 +50,7 @@ async def normalized_runner(*input_args, **input_kwargs): return normalized_runner - def get_stream_runner(self, entrypoint: EntryPoint): + def get_stream_runner(self, entrypoint: PythonicEntryPoint): """ Always returns an async generator, regardless of whether the underlying entrypoint is sync or async. diff --git a/runagent/sdk/server/framework/n8n.py b/runagent/sdk/server/framework/n8n.py index 55c697d..45a0025 100644 --- a/runagent/sdk/server/framework/n8n.py +++ b/runagent/sdk/server/framework/n8n.py @@ -5,29 +5,29 @@ from typing import Dict, List from rich.console import Console from runagent.utils.imports import PackageImporter -from runagent.utils.schema import EntryPoint, RunAgentConfig +from runagent.utils.schema import WebHookEntryPoint, RunAgentConfig from runagent.utils.serializer import CoreSerializer from runagent.utils.response import extract_jsonpath, to_dict console = Console() -class N8nExecutor: +class N8NExecutor: rerserved_tags = list() def __init__(self, agent_dir: Path): self.agent_dir = agent_dir - def get_runner(self, entrypoint: EntryPoint): + def get_runner(self, entrypoint: WebHookEntryPoint): """ Always returns an async function, regardless of whether the underlying entrypoint is sync or async. """ - resolved_entrypoint = self._entrypoint_resolver( - entrypoint_filepath=self.agent_dir / entrypoint.file, - entrypoint_module=entrypoint.module, - ) + # resolved_entrypoint = self._entrypoint_resolver( + # entrypoint_filepath=self.agent_dir / entrypoint.file, + # entrypoint_module=entrypoint.module, + # ) async def normalized_runner(*input_args, **input_kwargs): console.print(f"πŸ” [cyan]Entrypoint:[/cyan] {entrypoint.tag}") @@ -53,8 +53,14 @@ async def normalized_runner(*input_args, **input_kwargs): else: # normalized_runner() payload = None - - async with httpx.AsyncClient() as client: + + timeout = httpx.Timeout( + timeout=entrypoint.timeout, # 60 seconds total timeout + connect=10.0, # 10 seconds to connect + read=50.0, # 50 seconds to read response + write=10.0 # 10 seconds to write request + ) + async with httpx.AsyncClient(timeout=timeout) as client: if entrypoint.method == "post": # POST request with JSON payload response = await client.post( @@ -73,123 +79,5 @@ async def normalized_runner(*input_args, **input_kwargs): result = extract_jsonpath(result, entrypoint.extractor) return result - - - - - # async def normalized_runner(*input_args, **input_kwargs): - # console.print(f"πŸ” [cyan]Entrypoint:[/cyan] {entrypoint.tag}") - # console.print(f"πŸ” [cyan]Input data:[/cyan] *{input_args}, **{input_kwargs}") - - # if inspect.iscoroutinefunction(resolved_entrypoint): - # print("resolved async", (entrypoint.tag, entrypoint.module, resolved_entrypoint)) - # result = await resolved_entrypoint(*input_args, **input_kwargs) - # else: - # print("resolved sync (wrapped in async)", (entrypoint.tag, entrypoint.module, resolved_entrypoint)) - # # Run sync function in executor to avoid blocking - # result = await asyncio.get_event_loop().run_in_executor( - # None, lambda: resolved_entrypoint(*input_args, **input_kwargs) - # ) - - # if entrypoint.extractor: - # result = extract_jsonpath(result, entrypoint.extractor) - - # return result - - # return normalized_runner - - # def get_stream_runner(self, entrypoint: EntryPoint): - # """ - # Always returns an async generator, regardless of whether the - # underlying entrypoint is sync or async. - # """ - # resolved_entrypoint = self._entrypoint_resolver( - # entrypoint_filepath=self.agent_dir / entrypoint.file, - # entrypoint_module=entrypoint.module, - # ) - - # async def normalized_stream_runner(*input_args, **input_kwargs): - # console.print(f"πŸ” [cyan]Entrypoint:[/cyan] {entrypoint.tag}") - # console.print(f"πŸ” [cyan]Input data:[/cyan] *{input_args}, **{input_kwargs}") - - # if inspect.isasyncgenfunction(resolved_entrypoint): - # # Native async generator - # print("resolved async generator", (entrypoint.tag, entrypoint.module, resolved_entrypoint)) - # async for chunk in resolved_entrypoint(*input_args, **input_kwargs): - # if entrypoint.extractor: - # chunk = extract_jsonpath(chunk, entrypoint.extractor) - # yield chunk - # await asyncio.sleep(0) - - # elif inspect.iscoroutinefunction(resolved_entrypoint): - # # Async function that returns iterable - # print("resolved async function (returns iterable)", (entrypoint.tag, entrypoint.module, resolved_entrypoint)) - # result = await resolved_entrypoint(*input_args, **input_kwargs) - # for chunk in result: - # if entrypoint.extractor: - # chunk = extract_jsonpath(chunk, entrypoint.extractor) - # yield chunk - # await asyncio.sleep(0) - - # elif inspect.isgeneratorfunction(resolved_entrypoint): - # # Sync generator - consume in executor - # print("resolved sync generator (wrapped in async)", (entrypoint.tag, entrypoint.module, resolved_entrypoint)) - # generator = await asyncio.get_event_loop().run_in_executor( - # None, lambda: resolved_entrypoint(*input_args, **input_kwargs) - # ) - # for chunk in generator: - # if entrypoint.extractor: - # chunk = extract_jsonpath(chunk, entrypoint.extractor) - # yield chunk - # await asyncio.sleep(0) - - # else: - # # Sync function that returns iterable - # print("resolved sync function (returns iterable, wrapped in async)", (entrypoint.tag, entrypoint.module, resolved_entrypoint)) - # result = await asyncio.get_event_loop().run_in_executor( - # None, lambda: resolved_entrypoint(*input_args, **input_kwargs) - # ) - # for chunk in result: - # if entrypoint.extractor: - # chunk = extract_jsonpath(chunk, entrypoint.extractor) - # yield chunk - # await asyncio.sleep(0) - - # return normalized_stream_runner - - # def _entrypoint_resolver(self, entrypoint_filepath: Path, entrypoint_module: str): - # print(f"DEBUG: Resolving entrypoint - filepath: {entrypoint_filepath}, module: {entrypoint_module}") - # primary_module, secondary_attr = ( - # entrypoint_module.split(".", 1) + [""] - # )[:2] - # print(f"DEBUG: Split module - primary: {primary_module}, secondary: {secondary_attr}") - - # resolved_module = self.importer.resolve_import( - # entrypoint_filepath, primary_module - # ) - # print(f"DEBUG: Resolved primary module: {resolved_module}") - - # if secondary_attr: - # attrs = secondary_attr.split('.') - # print(f"DEBUG: Secondary attributes to resolve: {attrs}") - # for attr in attrs: - # resolved_module = getattr(resolved_module, attr) - # print(f"DEBUG: Resolved attribute {attr}: {resolved_module}") - - # print(f"DEBUG: Final resolved module: {resolved_module}") - # print("Resolving", (entrypoint_module, resolved_module)) - # return resolved_module - # def _get_function_type(self, func): - # """ - # Determine the type of function for debugging purposes. - # Returns: str describing the function type - # """ - # if inspect.isasyncgenfunction(func): - # return "async_generator" - # elif inspect.iscoroutinefunction(func): - # return "async_function" - # elif inspect.isgeneratorfunction(func): - # return "sync_generator" - # else: - # return "sync_function" \ No newline at end of file + return normalized_runner diff --git a/runagent/utils/schema.py b/runagent/utils/schema.py index f108a91..dc9b9b2 100644 --- a/runagent/utils/schema.py +++ b/runagent/utils/schema.py @@ -1,6 +1,7 @@ # Pydantic schema for runagent.config.json from datetime import datetime -from typing import Any, Dict, List, Optional, Union +import typing as t +# from typing import Any, Dict, List, Optional, Union from enum import Enum from pydantic import BaseModel, Field, validator @@ -13,23 +14,37 @@ class TemplateSource(BaseModel): path: str = Field(..., description="Path to template in repository") -class EntryPoint(BaseModel): - """Entrypoint configuration""" +class PythonicEntryPoint(BaseModel): + """Configuration for a Python-based entrypoint.""" - file: str = Field(..., description="Entrypoint file") - module: str = Field(..., description="Entrypoint module name") + file: str = Field(..., description="Path to the Python file containing the entrypoint") + module: str = Field(..., description="Python module name for the entrypoint") + tag: str = Field(..., description="Unique tag identifying this entrypoint") + extractor: t.Optional[t.Dict[str, str]] = Field( + default={}, + description="Optional mapping of output fields to JSONPath expressions for extracting data from the response" + ) + + +class WebHookEntryPoint(BaseModel): + """Configuration for a webhook-based entrypoint.""" + + webhook_url: str = Field(..., description="Webhook URL for the entrypoint") + method: str = Field(..., description="HTTP method to use for the webhook") tag: str = Field(..., description="Entrypoint tag") - extractor: Optional[Dict[str, str]] = Field( + timeout: int = Field(30, description="Timeout in seconds for the webhook request") + extractor: t.Optional[t.Dict[str, str]] = Field( default={}, description="JSONPath expression to extract data from the response" ) - class AgentArchitecture(BaseModel): """Agent architecture configuration""" - entrypoints: List[EntryPoint] = Field( + entrypoints: t.Union[ + t.List[PythonicEntryPoint], t.List[WebHookEntryPoint] + ] = Field( ..., description="List of entrypoints" ) @@ -51,13 +66,13 @@ class RunAgentConfig(BaseModel): template: str = Field(..., description="Template name") version: str = Field(..., description="Agent version") created_at: datetime = Field(..., description="Creation timestamp") - template_source: Optional[TemplateSource] = Field( + template_source: t.Optional[TemplateSource] = Field( ..., description="Template source details" ) agent_architecture: AgentArchitecture = Field( ..., description="Agent architecture details" ) - env_vars: Optional[Dict[str, str]] = Field( + env_vars: t.Optional[t.Dict[str, str]] = Field( default_factory=dict, description="Environment variables" ) @@ -65,10 +80,10 @@ class RunAgentConfig(BaseModel): class AgentInputArgs(BaseModel): """Request model for agent execution""" - input_args: List[Any] = Field( + input_args: t.List[t.Any] = Field( default={}, description="Input data for agent invocation" ) - input_kwargs: Dict[str, Any] = Field( + input_kwargs: t.Dict[str, t.Any] = Field( default={}, description="Input data for agent invocation" ) @@ -84,7 +99,7 @@ class WebSocketAgentRequest(BaseModel): action: WebSocketActionType agent_id: str input_data: AgentInputArgs - stream_config: Optional[Dict[str, Any]] = Field(default_factory=dict) + stream_config: t.Optional[t.Dict[str, t.Any]] = Field(default_factory=dict) # Pydantic Models @@ -100,9 +115,9 @@ class AgentRunResponse(BaseModel): """Response model for agent execution""" success: bool - output_data: Optional[Any] = None - error: Optional[str] = None - execution_time: Optional[float] = None + output_data: t.Optional[t.Any] = None + error: t.Optional[str] = None + execution_time: t.Optional[float] = None agent_id: str @@ -113,7 +128,7 @@ class CapacityInfo(BaseModel): max_capacity: int remaining_slots: int is_full: bool - agents: List[Dict[str, Any]] + agents: t.List[t.Dict[str, t.Any]] class AgentInfo(BaseModel): @@ -123,8 +138,8 @@ class AgentInfo(BaseModel): version: str host: str port: int - config: Dict[str, Any] - endpoints: Dict[str, str] + config: t.Dict[str, t.Any] + endpoints: t.Dict[str, str] # Message types for different agentic framework responses @@ -144,11 +159,11 @@ class SafeMessage(BaseModel): id: str type: MessageType timestamp: str - data: Any - metadata: Optional[Dict[str, Any]] = None - error: Optional[str] = None + data: t.Any + metadata: t.Optional[t.Dict[str, t.Any]] = None + error: t.Optional[str] = None - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> t.Dict[str, t.Any]: return { "id": self.id, "type": self.type.value, @@ -156,4 +171,4 @@ def to_dict(self) -> Dict[str, Any]: "data": self.data, "metadata": self.metadata, "error": self.error - } \ No newline at end of file + } From d92cd5c910c4d13836569b9b00062b254e085c9a Mon Sep 17 00:00:00 2001 From: sawradip Date: Sun, 27 Jul 2025 11:32:43 +0600 Subject: [PATCH 03/16] feat: aded n8n template --- templates/n8n/default/runagent.config.json | 4 +- templates/n8n/default/simple_assistant.py | 24 --------- templates/n8n/email_agent/email_agent.py | 54 ------------------- templates/n8n/email_agent/requirements.txt | 1 - .../n8n/email_agent/runagent.config.json | 30 ----------- 5 files changed, 1 insertion(+), 112 deletions(-) delete mode 100644 templates/n8n/default/simple_assistant.py delete mode 100644 templates/n8n/email_agent/email_agent.py delete mode 100644 templates/n8n/email_agent/requirements.txt delete mode 100644 templates/n8n/email_agent/runagent.config.json diff --git a/templates/n8n/default/runagent.config.json b/templates/n8n/default/runagent.config.json index 248067b..457a827 100644 --- a/templates/n8n/default/runagent.config.json +++ b/templates/n8n/default/runagent.config.json @@ -15,11 +15,9 @@ { "webhook_url": "https://taniatinni.app.n8n.cloud/webhook/recipe-nutrition", "method": "post", + "timeout": 60, "tag": "food_doctor" } ] - }, - "env_vars": { - "OPENAI_API_KEY": "sk-proj-1234567890" } } \ No newline at end of file diff --git a/templates/n8n/default/simple_assistant.py b/templates/n8n/default/simple_assistant.py deleted file mode 100644 index 6669cb3..0000000 --- a/templates/n8n/default/simple_assistant.py +++ /dev/null @@ -1,24 +0,0 @@ -from openai import OpenAI - - -def get_response(user_msg: str) -> str: - client = OpenAI() - return client.chat.completions.create( - model="gpt-4o-mini", - messages=[ - {"role": "developer", "content": "You are a helpful assistant."}, - {"role": "user", "content": user_msg} - ] - ) - - -def get_response_stream(user_msg: str) -> str: - client = OpenAI() - return client.chat.completions.create( - model="gpt-4o-mini", - stream=True, - messages=[ - {"role": "developer", "content": "You are a helpful assistant."}, - {"role": "user", "content": user_msg} - ] - ) diff --git a/templates/n8n/email_agent/email_agent.py b/templates/n8n/email_agent/email_agent.py deleted file mode 100644 index c157170..0000000 --- a/templates/n8n/email_agent/email_agent.py +++ /dev/null @@ -1,54 +0,0 @@ -from openai import OpenAI -from openai.types.responses import ResponseTextDeltaEvent -client = OpenAI() - - -def get_prompt(sender_name: str, recipient_name: str, subject: str) -> str: - return ( - f"Write a professional email with the following details:\n" - f"- Sender: {sender_name}\n" - f"- Recipient: {recipient_name}\n" - f"- Subject: {subject}\n\n" - f"The email should be concise, polite, and use natural language. Include a greeting, body, and closing." - ) - - -def generate_email(sender_name: str, recipient_name: str, subject: str) -> str: - prompt = get_prompt(sender_name, recipient_name, subject) - - response = client.responses.create( - model="gpt-4", - input=prompt - ) - - email_text = response.output_text.strip() - return email_text - - -def generate_email_sream(sender_name: str, recipient_name: str, subject: str) -> str: - prompt = get_prompt(sender_name, recipient_name, subject) - - stream_response = client.responses.create( - model="gpt-4", - input=prompt, - stream=True - ) - - for chunk in stream_response: - if isinstance(chunk, ResponseTextDeltaEvent): - yield chunk.delta - - -# Example usage -if __name__ == "__main__": - sender = "Alice Johnson" - recipient = "Mr. Daniel Smith" - subject = "Request for Meeting Next Week" - - email = generate_email(sender, recipient, subject) - print("\n--- Generated Email ---\n") - print(email) - - # print("\n--- Generated Email Stream---\n") - # for chunk in generate_email_sream(sender, recipient, subject): - # print(chunk) diff --git a/templates/n8n/email_agent/requirements.txt b/templates/n8n/email_agent/requirements.txt deleted file mode 100644 index ec838c5..0000000 --- a/templates/n8n/email_agent/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -openai diff --git a/templates/n8n/email_agent/runagent.config.json b/templates/n8n/email_agent/runagent.config.json deleted file mode 100644 index 5b0a8b8..0000000 --- a/templates/n8n/email_agent/runagent.config.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "agent_name": "Demo Agent", - "description": "A simple placeholder agent", - "framework": "openai", - "template": "default", - "version": "1.0.0", - "created_at": "2025-06-25 13:42:03", - "template_source": { - "repo_url": "https://github.com/runagent-dev/runagent.git", - "path": "templates/default", - "author": "sawradip" - }, - "agent_architecture": { - "entrypoints": [ - { - "file": "email_agent.py", - "module": "generate_email", - "tag": "generic_email" - }, - { - "file": "email_agent.py", - "module": "generate_email_sream", - "tag": "email_stream" - } - ] - }, - "env_vars": { - "OPENAI_API_KEY": "sk-proj-1234567890" - } -} \ No newline at end of file From b8745d4553fd2d24585a9902c5ba75099ae99290 Mon Sep 17 00:00:00 2001 From: sawradip Date: Sun, 27 Jul 2025 11:33:11 +0600 Subject: [PATCH 04/16] feat: n8n js test added --- .gitignore | 2 +- test_scripts/js/package.json | 3 +- test_scripts/js/pnpm-lock.yaml | 301 ++++++++++++++++++++++++++++++ test_scripts/js/test-node-n8n.mjs | 37 ++++ 4 files changed, 341 insertions(+), 2 deletions(-) create mode 100644 test_scripts/js/pnpm-lock.yaml create mode 100644 test_scripts/js/test-node-n8n.mjs diff --git a/.gitignore b/.gitignore index 8904ed7..0d32626 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ __pycache__/ # C extensions *.so - +node_modules/ # Distribution / packaging .Python build/ diff --git a/test_scripts/js/package.json b/test_scripts/js/package.json index e73d4b2..23a7c1d 100644 --- a/test_scripts/js/package.json +++ b/test_scripts/js/package.json @@ -10,6 +10,7 @@ "license": "ISC", "description": "", "dependencies": { - "better-sqlite3": "^12.2.0" + "better-sqlite3": "^12.2.0", + "runagent": "^0.1.4" } } diff --git a/test_scripts/js/pnpm-lock.yaml b/test_scripts/js/pnpm-lock.yaml new file mode 100644 index 0000000..acb9fa0 --- /dev/null +++ b/test_scripts/js/pnpm-lock.yaml @@ -0,0 +1,301 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + better-sqlite3: + specifier: ^12.2.0 + version: 12.2.0 + runagent: + specifier: ^0.1.4 + version: 0.1.4 + +packages: + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + better-sqlite3@12.2.0: + resolution: {integrity: sha512-eGbYq2CT+tos1fBwLQ/tkBt9J5M3JEHjku4hbvQUePCckkvVf14xWj+1m7dGoK81M/fOjFT7yM9UMeKT/+vFLQ==} + engines: {node: 20.x || 22.x || 23.x || 24.x} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + + node-abi@3.75.0: + resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==} + engines: {node: '>=10'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + hasBin: true + + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + runagent@0.1.4: + resolution: {integrity: sha512-KvD1AEIswFvPrhtp9RoPyLIbkwzy4AMWj8G9CgIiyggROTnMWj7IvwYvKmDkXC4vx/MnaQt9muguSpKG/LYLAA==} + peerDependencies: + ws: ^8.18.3 + peerDependenciesMeta: + ws: + optional: true + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + tar-fs@2.1.3: + resolution: {integrity: sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + +snapshots: + + base64-js@1.5.1: {} + + better-sqlite3@12.2.0: + dependencies: + bindings: 1.5.0 + prebuild-install: 7.1.3 + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + chownr@1.1.4: {} + + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + + deep-extend@0.6.0: {} + + detect-libc@2.0.4: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + expand-template@2.0.3: {} + + file-uri-to-path@1.0.0: {} + + fs-constants@1.0.0: {} + + github-from-package@0.0.0: {} + + ieee754@1.2.1: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + mimic-response@3.1.0: {} + + minimist@1.2.8: {} + + mkdirp-classic@0.5.3: {} + + napi-build-utils@2.0.0: {} + + node-abi@3.75.0: + dependencies: + semver: 7.7.2 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + prebuild-install@7.1.3: + dependencies: + detect-libc: 2.0.4 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.75.0 + pump: 3.0.3 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.3 + tunnel-agent: 0.6.0 + + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + runagent@0.1.4: + dependencies: + better-sqlite3: 12.2.0 + + safe-buffer@5.2.1: {} + + semver@7.7.2: {} + + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-json-comments@2.0.1: {} + + tar-fs@2.1.3: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.3 + tar-stream: 2.2.0 + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + + util-deprecate@1.0.2: {} + + wrappy@1.0.2: {} diff --git a/test_scripts/js/test-node-n8n.mjs b/test_scripts/js/test-node-n8n.mjs new file mode 100644 index 0000000..475f079 --- /dev/null +++ b/test_scripts/js/test-node-n8n.mjs @@ -0,0 +1,37 @@ +import { RunAgentClient } from 'runagent'; + +// Example 1: Non-streaming +async function testStandard() { + console.log('=== Testing Standard Execution ==='); + + const ra = new RunAgentClient({ + agentId: '95d60499-b9c1-4cc4-8e7a-0382b6e085d7', + entrypointTag: 'food_doctor', + // host: 'localhost', + // port: 8451, + local: true, + }); + + await ra.initialize(); + console.log(`βœ… Environment: ${ra.environment}`); + + const result = await ra.run({ + payload: { + "recipe_name": "Chocolate Chip Pancakes", + "servings": 4, + "ingredients": [ + "2 cups all-purpose flour", + "2 tablespoons sugar", + "2 teaspoons baking powder", + "1 teaspoon salt", + "2 large eggs", + "1.5 cups whole milk", + "1/4 cup melted butter", + "1/2 cup chocolate chips" + ]} + }); + + console.log('Result:', result); +} + +await testStandard(); From 460a96d7946448c52971b20db200ef631711baf1 Mon Sep 17 00:00:00 2001 From: sawradip Date: Sun, 27 Jul 2025 11:33:28 +0600 Subject: [PATCH 05/16] feat: n8n python test added --- test_scripts/python/client_test_n8n.py | 43 ++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 test_scripts/python/client_test_n8n.py diff --git a/test_scripts/python/client_test_n8n.py b/test_scripts/python/client_test_n8n.py new file mode 100644 index 0000000..9c55a51 --- /dev/null +++ b/test_scripts/python/client_test_n8n.py @@ -0,0 +1,43 @@ +from pprint import pprint +from runagent import RunAgentClient + +ra = RunAgentClient( + agent_id="95d60499-b9c1-4cc4-8e7a-0382b6e085d7", + entrypoint_tag="food_doctor", + local=True + ) + + +agent_results = ra.run( + payload={ + "recipe_name": "Chocolate Chip Pancakes", + "servings": 4, + "ingredients": [ + "2 cups all-purpose flour", + "2 tablespoons sugar", + "2 teaspoons baking powder", + "1 teaspoon salt", + "2 large eggs", + "1.5 cups whole milk", + "1/4 cup melted butter", + "1/2 cup chocolate chips" + ] + } +) + +pprint(agent_results) + +# ================================== + +# from runagent import RunAgentClient + +# ra = RunAgentClient( +# agent_id="40ca8515-a19a-49b2-8ef4-030f83bfc074", +# entrypoint_tag="simple_assistant_extracted_stream", +# local=True +# ) + +# for chunk in ra.run( +# user_msg="Analyze the benefits of remote work for software teams" +# ): +# print(chunk) From 52f9fc5338565dc310577b3df24f1bb5492cee43 Mon Sep 17 00:00:00 2001 From: sawradip Date: Sun, 27 Jul 2025 11:36:50 +0600 Subject: [PATCH 06/16] feat: --n8n template added --- runagent/cli/commands.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/runagent/cli/commands.py b/runagent/cli/commands.py index ed860e0..e412e28 100644 --- a/runagent/cli/commands.py +++ b/runagent/cli/commands.py @@ -197,6 +197,7 @@ def delete(agent_id, yes): @click.option("--letta", is_flag=True, help="Use Letta framework") @click.option("--llamaindex", is_flag=True, help="Use LlamaIndex framework") @click.option("--openai", is_flag=True, help="Use OpenAI framework") +@click.option("--n8n", is_flag=True, help="Use N8N workflows") @click.argument( "path", type=click.Path( @@ -222,6 +223,7 @@ def init( letta, llamaindex, openai, + n8n, path ): """Initialize a new RunAgent project""" @@ -240,6 +242,7 @@ def init( "letta": letta, "llamaindex": llamaindex, "openai": openai, + "n8n": n8n } total_flags = sum(flag for flag in framework_dict.values()) if total_flags > 1: From 7bcf2554ca7152030993cd84fc885d7009e86b44 Mon Sep 17 00:00:00 2001 From: sawradip Date: Tue, 29 Jul 2025 14:22:12 +0600 Subject: [PATCH 07/16] feat: n8n support wip --- .github/workflows/create-release.yml | 143 ++++++++++++++ release.sh | 258 +++++++++++++++++++++++++ runagent/cli/commands.py | 1 + runagent/client/client.py | 1 - runagent/sdk/sdk.py | 2 +- runagent/sdk/server/local_server.py | 9 +- runagent/sdk/template_manager.py | 2 +- runagent/utils/agent.py | 94 +++++---- runagent/utils/enums.py | 20 ++ runagent/utils/schema.py | 3 +- templates/n8n/default/requirements.txt | 1 - test_scripts/python/client_test_n8n.py | 18 +- 12 files changed, 475 insertions(+), 77 deletions(-) create mode 100644 .github/workflows/create-release.yml create mode 100644 release.sh diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml new file mode 100644 index 0000000..c69523d --- /dev/null +++ b/.github/workflows/create-release.yml @@ -0,0 +1,143 @@ +# .github/workflows/create-release.yml +name: Create GitHub Release + +on: + push: + tags: + - 'v*' # Triggers on tags like v1.2.3 + +jobs: + create-main-release: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Need full history for changelog + + - name: Extract version from tag + id: extract_version + run: | + VERSION=${GITHUB_REF#refs/tags/v} + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "tag_name=v$VERSION" >> $GITHUB_OUTPUT + echo "Extracted version: $VERSION" + + - name: Extract changelog for this version + id: changelog + run: | + if [ -f "CHANGELOG.md" ]; then + echo "πŸ“ Extracting changelog from committed CHANGELOG.md" + + # Extract the section for this version from CHANGELOG.md + # This assumes the changelog was generated locally and committed + awk '/^## \[.*\].*/{if(found) exit; if(/\['${{ steps.extract_version.outputs.version }}'\]/) found=1; if(found) print; next} found' CHANGELOG.md > current_changelog.md + + # If the extraction didn't work well, try a different approach + if [ ! -s current_changelog.md ]; then + # Extract everything after the first ## heading until the next ## + sed -n '/^## \[/,/^## \[/p' CHANGELOG.md | head -n -1 > current_changelog.md + fi + + # If still empty, use the first 30 lines of changelog + if [ ! -s current_changelog.md ]; then + head -30 CHANGELOG.md > current_changelog.md + fi + + # Read the changelog content + CHANGELOG_CONTENT=$(cat current_changelog.md) + + # Clean up and format for GitHub + echo "changelog<> $GITHUB_OUTPUT + echo "$CHANGELOG_CONTENT" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + echo "βœ… Changelog extracted successfully" + echo "Preview:" + echo "--------" + head -10 current_changelog.md + + else + echo "⚠️ No CHANGELOG.md found, generating basic changelog" + + # Fallback: generate basic changelog from git history + LAST_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "") + + if [ -n "$LAST_TAG" ]; then + echo "## What's Changed" > current_changelog.md + echo "" >> current_changelog.md + git log --pretty=format:"- %s" $LAST_TAG..HEAD >> current_changelog.md + else + echo "## Initial Release" > current_changelog.md + echo "" >> current_changelog.md + echo "First release of RunAgent Universal AI Agent Platform" >> current_changelog.md + fi + + CHANGELOG_CONTENT=$(cat current_changelog.md) + echo "changelog<> $GITHUB_OUTPUT + echo "$CHANGELOG_CONTENT" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + fi + + - name: Create main GitHub Release + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ steps.extract_version.outputs.tag_name }} + name: RunAgent v${{ steps.extract_version.outputs.version }} + body: | + # πŸš€ RunAgent v${{ steps.extract_version.outputs.version }} + + **Universal AI Agent Platform - All SDKs synchronized at v${{ steps.extract_version.outputs.version }}** + + ## πŸ“¦ Installation + + Choose your preferred language: + + ### Python + ```bash + pip install runagent==${{ steps.extract_version.outputs.version }} + ``` + + ### JavaScript/TypeScript + ```bash + npm install runagent@${{ steps.extract_version.outputs.version }} + ``` + + ### Rust + ```toml + [dependencies] + runagent = "${{ steps.extract_version.outputs.version }}" + ``` + + ### Go + ```bash + go get github.com/runagent-dev/runagent/runagent-go@${{ steps.extract_version.outputs.tag_name }} + ``` + + ## πŸ“‹ Changes in this Release + + ${{ steps.changelog.outputs.changelog }} + + ## πŸ”— Package Links + + - 🐍 [PyPI Package](https://pypi.org/project/runagent/${{ steps.extract_version.outputs.version }}/) + - πŸ“¦ [npm Package](https://www.npmjs.com/package/runagent/v/${{ steps.extract_version.outputs.version }}) + - πŸ¦€ [crates.io Package](https://crates.io/crates/runagent/${{ steps.extract_version.outputs.version }}) + - 🐹 [Go Module](https://pkg.go.dev/github.com/runagent-dev/runagent/runagent-go@${{ steps.extract_version.outputs.tag_name }}) + + ## πŸ“š Documentation + + - πŸ“– [Documentation](https://docs.run-agent.ai) + - πŸš€ [Quick Start Guide](https://docs.run-agent.ai/get-started/quickstart) + - πŸ’¬ [Discord Community](https://discord.gg/runagent) + + --- + + **Full Changelog**: https://github.com/runagent-dev/runagent/compare/v${{ steps.extract_version.outputs.version }} + draft: false + prerelease: false + generate_release_notes: false + files: | + CHANGELOG.md + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN } \ No newline at end of file diff --git a/release.sh b/release.sh new file mode 100644 index 0000000..0bad715 --- /dev/null +++ b/release.sh @@ -0,0 +1,258 @@ +#!/bin/bash +# release.sh - Version bumping and tagging script + +set -e + +usage() { + echo "Usage: $0 " + echo "Version: semantic version (e.g., 1.2.3)" + echo "" + echo "This will:" + echo " 1. Update version in all SDK package files" + echo " 2. Commit the changes" + echo " 3. Create a git tag v" + echo " 4. Push everything to main branch" + echo " 5. Workflows will generate changelog and publish packages" + echo "" + echo "Example:" + echo " $0 1.2.3" +} + +validate_version() { + local version=$1 + if [[ ! $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Version must be in format X.Y.Z (e.g., 1.2.3)" + return 1 + fi + return 0 +} + +update_python_version() { + local version=$1 + local pyproject_file="pyproject.toml" + local version_file="runagent/__version__.py" + + if [[ -f "$pyproject_file" ]]; then + echo "πŸ“¦ Updating Python pyproject.toml version to $version" + sed -i.bak "s/version = \".*\"/version = \"$version\"/" "$pyproject_file" + rm "$pyproject_file.bak" 2>/dev/null || true + else + echo "⚠️ Warning: $pyproject_file not found, skipping Python pyproject.toml update" + fi + + if [[ -f "$version_file" ]]; then + echo "πŸ“¦ Updating Python __version__.py to $version" + sed -i.bak "s/__version__ = \".*\"/__version__ = \"$version\"/" "$version_file" + rm "$version_file.bak" 2>/dev/null || true + else + echo "πŸ“¦ Creating Python __version__.py with version $version" + echo "__version__ = \"$version\"" > "$version_file" + fi +} + +update_javascript_version() { + local version=$1 + local file="runagent-ts/package.json" + + if [[ -f "$file" ]]; then + echo "πŸ“¦ Updating JavaScript version to $version" + cd runagent-ts + npm version "$version" --no-git-tag-version --allow-same-version + cd .. + else + echo "⚠️ Warning: $file not found, skipping JavaScript version update" + fi +} + +update_rust_version() { + local version=$1 + local file="runagent-rust/runagent/Cargo.toml" + + if [[ -f "$file" ]]; then + echo "πŸ“¦ Updating Rust version to $version" + sed -i.bak "s/^version = \".*\"/version = \"$version\"/" "$file" + rm "$file.bak" 2>/dev/null || true + else + echo "⚠️ Warning: $file not found, skipping Rust version update" + fi +} + +update_go_version() { + local version=$1 + local version_file="runagent-go/runagent/version.go" + local go_mod_file="runagent-go/go.mod" + + mkdir -p runagent-go/runagent + echo "πŸ“¦ Updating Go version reference to $version" + cat > "$version_file" << EOF +package runagent + +// Version represents the current version of the RunAgent Go SDK +const Version = "$version" +EOF + + if [[ ! -f "$go_mod_file" ]]; then + echo "πŸ“¦ Creating Go module file" + cat > "$go_mod_file" << EOF +module github.com/runagent-dev/runagent/runagent-go + +go 1.20 + +// Dependencies will be added here +EOF + fi +} + +check_prerequisites() { + # Check if we're on main branch (strict requirement now) + local current_branch=$(git branch --show-current) + if [[ "$current_branch" != "main" ]] && [[ "$current_branch" != "master" ]]; then + echo "❌ Error: You must be on the main branch to create a release" + echo "Current branch: $current_branch" + echo "" + echo "Please switch to main branch:" + echo " git checkout main" + exit 1 + fi + + # Check for uncommitted changes + if [[ -n $(git status --porcelain) ]]; then + echo "❌ Error: You have uncommitted changes" + git status --short + echo "" + echo "Please commit or stash your changes before releasing." + exit 1 + fi + + # Check if required commands exist + if ! command -v npm &> /dev/null; then + echo "⚠️ Warning: npm not found, JavaScript version update may fail" + fi + + # Pull latest changes + echo "πŸ”„ Pulling latest changes from main..." + git pull origin main +} + +# Main script +if [[ $# -ne 1 ]]; then + usage + exit 1 +fi + +VERSION=$1 + +# Validate version format +if ! validate_version "$VERSION"; then + exit 1 +fi + +echo "πŸš€ RunAgent Version Bump to v$VERSION" +echo "====================================" + +# Run prerequisite checks +check_prerequisites + +# Check if tag already exists +if git tag -l | grep -q "^v$VERSION$"; then + echo "❌ Error: Tag v$VERSION already exists" + echo "Existing tags:" + git tag -l | grep "^v" | sort -V | tail -5 + exit 1 +fi + +echo "" +echo "πŸ“ Updating version files..." + +# Update all package files +update_python_version "$VERSION" +update_javascript_version "$VERSION" +update_rust_version "$VERSION" +update_go_version "$VERSION" + +echo "" +echo "πŸ“‹ Summary of changes:" +git diff --name-only + +echo "" +echo "πŸ” Version changes:" +echo "-------------------" + +# Show version changes +if [[ -f "pyproject.toml" ]]; then + echo "Python (pyproject.toml): $(grep 'version = ' pyproject.toml)" +fi + +if [[ -f "runagent/__version__.py" ]]; then + echo "Python (__version__.py): $(grep '__version__ = ' runagent/__version__.py)" +fi + +if [[ -f "runagent-ts/package.json" ]]; then + echo "JavaScript: $(grep '"version":' runagent-ts/package.json)" +fi + +if [[ -f "runagent-rust/runagent/Cargo.toml" ]]; then + echo "Rust: $(grep '^version = ' runagent-rust/runagent/Cargo.toml)" +fi + +if [[ -f "runagent-go/runagent/version.go" ]]; then + echo "Go: $(grep 'const Version = ' runagent-go/runagent/version.go)" +fi + +echo "" +read -p "πŸ€” Do these changes look correct? (y/N): " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "❌ Release cancelled. Reverting changes..." + git checkout -- . + exit 1 +fi + +echo "" +echo "πŸ’Ύ Committing version changes..." + +git add . +git commit -m "chore: bump version to v$VERSION + +- Update Python SDK to v$VERSION +- Update JavaScript SDK to v$VERSION +- Update Rust SDK to v$VERSION +- Update Go SDK to v$VERSION" + +echo "" +echo "🏷️ Creating tag v$VERSION..." +git tag -a "v$VERSION" -m "Release v$VERSION + +RunAgent Universal Release v$VERSION + +All SDKs updated to version $VERSION: +- Python SDK: v$VERSION +- JavaScript SDK: v$VERSION +- Rust SDK: v$VERSION +- Go SDK: v$VERSION + +Changelog will be generated automatically by workflows." + +echo "" +echo "⬆️ Pushing to main branch with tag..." +git push origin main +git push origin "v$VERSION" + +echo "" +echo "βœ… Version v$VERSION tagged and pushed to main!" +echo "" +echo "🎯 What happens next:" +echo " 1. GitHub Actions will detect the v$VERSION tag on main branch" +echo " 2. Unified release workflow will:" +echo " - Generate changelog from conventional commits" +echo " - Create main GitHub release with changelog" +echo " 3. Individual SDK workflows will:" +echo " - Test and publish to PyPI, npm, crates.io" +echo " - Update the main release with their status" +echo "" +echo "πŸ“Š Monitor progress:" +echo " - Actions: https://github.com/runagent-dev/runagent/actions" +echo " - Releases: https://github.com/runagent-dev/runagent/releases" +echo "" +echo "🏷️ Tag: v$VERSION (on main branch)" +echo "πŸ“ Changelog: Will be generated automatically by workflows" \ No newline at end of file diff --git a/runagent/cli/commands.py b/runagent/cli/commands.py index e412e28..b0dcdef 100644 --- a/runagent/cli/commands.py +++ b/runagent/cli/commands.py @@ -303,6 +303,7 @@ def init( console.print(f" Framework: [magenta]{framework if framework else 'None'}[/magenta]") console.print(f" Template: [yellow]{template}[/yellow]") + print(">>", framework, ">>", template) # Initialize project success = sdk.init_project( folder_path=project_path, diff --git a/runagent/client/client.py b/runagent/client/client.py index 03e9a9e..e5c88da 100644 --- a/runagent/client/client.py +++ b/runagent/client/client.py @@ -1,6 +1,5 @@ from runagent.sdk import RunAgentSDK from runagent.sdk.rest_client import RestClient -from runagent.utils.agent import detect_framework, validate_agent from runagent.sdk.socket_client import SocketClient from runagent.utils.serializer import CoreSerializer from rich.console import Console diff --git a/runagent/sdk/sdk.py b/runagent/sdk/sdk.py index 493d441..9e401b4 100644 --- a/runagent/sdk/sdk.py +++ b/runagent/sdk/sdk.py @@ -171,7 +171,7 @@ def init_project( ValidationError: If parameters are invalid FileExistsError: If folder exists and overwrite is False """ - return self.templates.init_project( + return self.templates.init_template( folder_path=folder_path, framework=framework, template=template, overwrite=overwrite ) diff --git a/runagent/sdk/server/local_server.py b/runagent/sdk/server/local_server.py index 4509146..5e4da4e 100644 --- a/runagent/sdk/server/local_server.py +++ b/runagent/sdk/server/local_server.py @@ -27,6 +27,9 @@ from runagent.utils.port import PortManager from runagent.utils.serializer import CoreSerializer +from runagent.utils.port import PortManager +from runagent.utils.agent import detect_framework, get_agent_config + console = Console() @@ -53,7 +56,7 @@ def __init__( self.agent_name = self.agent_config.agent_name self.agent_version = self.agent_config.version - self.agent_framework = self.agent_config.framework + self.agent_framework = self.agent_config.framework.value self.agent_architecture = self.agent_config.agent_architecture # Install dependencies if requirements.txt exists @@ -177,10 +180,6 @@ def from_path( Returns: LocalServer instance """ - import uuid - from runagent.utils.port import PortManager - from runagent.utils.agent import detect_framework, get_agent_config - db_service = DBService() agent_path = agent_path.resolve() diff --git a/runagent/sdk/template_manager.py b/runagent/sdk/template_manager.py index de4bdab..24ddec9 100644 --- a/runagent/sdk/template_manager.py +++ b/runagent/sdk/template_manager.py @@ -77,7 +77,7 @@ def get_info(self, framework: str, template: str) -> t.Optional[t.Dict[str, t.An raise return None - def init_project( + def init_template( self, folder_path: Path, framework: str, template: str, overwrite: bool = False ) -> bool: """ diff --git a/runagent/utils/agent.py b/runagent/utils/agent.py index 51640ba..b7113e3 100644 --- a/runagent/utils/agent.py +++ b/runagent/utils/agent.py @@ -1,13 +1,10 @@ -import os -import importlib import json -import sys import typing as t from pathlib import Path - from runagent.constants import AGENT_CONFIG_FILE_NAME from runagent.utils.imports import PackageImporter from runagent.utils.schema import RunAgentConfig +from .enums import FrameworkType def get_agent_config(folder_path: Path) -> t.Optional[dict]: @@ -109,7 +106,7 @@ def detect_framework(folder_path: Path) -> str: config = get_agent_config(folder_path) framework = config.framework - return framework + return framework.value def validate_agent( @@ -131,6 +128,13 @@ def validate_agent( "error_msgs": [f"Agent Folder not found: {folder}"], } + config = get_agent_config(folder_path) + + is_valid, details = validate_pythonic_agent(config, dynamic_loading, folder_path) + + +def validate_pythonic_agent(config, dynamic_loading, folder_path): + validation_details = { "valid": False, "folder_exists": True, @@ -145,61 +149,51 @@ def validate_agent( unwanted_files = [".env"] # Check for runagent.config.json and validate schema - try: - config = get_agent_config(folder_path) - validation_details["files_found"].append(AGENT_CONFIG_FILE_NAME) - validation_details["success_msgs"].append( - f"Found and validated {AGENT_CONFIG_FILE_NAME}" - ) + # try: - # Validate each entrypoint dynamically - for entrypoint in config.agent_architecture.entrypoints: - entrypoint_file = folder_path / entrypoint.file - module_name = entrypoint.module + # Validate each entrypoint dynamically + for entrypoint in config.agent_architecture.entrypoints: + entrypoint_file = folder_path / entrypoint.file + module_name = entrypoint.module - if not entrypoint_file.exists(): - validation_details["error_msgs"].append( - f"Entrypoint file not found: {entrypoint_file}" + if not entrypoint_file.exists(): + validation_details["error_msgs"].append( + f"Entrypoint file not found: {entrypoint_file}" + ) + validation_details["missing_files"].append(entrypoint.file) + validation_details["valid"] = False + continue + + if dynamic_loading: + try: + importer = PackageImporter() + importer.resolve_import(entrypoint_file, module_name) + validation_details["success_msgs"].append( + f"Found {module_name} reference in {entrypoint_file}" ) - validation_details["missing_files"].append(entrypoint.file) + except Exception as e: + validation_details["error_msgs"].append(str(e)) validation_details["valid"] = False - continue - - if dynamic_loading: - try: - importer = PackageImporter() - importer.resolve_import(entrypoint_file, module_name) + else: + try: + content = entrypoint_file.read_text() + if module_name in content: validation_details["success_msgs"].append( f"Found {module_name} reference in {entrypoint_file}" ) - except Exception as e: - validation_details["error_msgs"].append(str(e)) - validation_details["valid"] = False - else: - try: - content = entrypoint_file.read_text() - if module_name in content: - validation_details["success_msgs"].append( - f"Found {module_name} reference in {entrypoint_file}" - ) - else: - validation_details["error_msgs"].append( - f"Module {module_name} not found in {entrypoint_file}" - ) - validation_details["missing_files"].append( - f"{module_name} module reference" - ) - validation_details["valid"] = False - except Exception as e: + else: validation_details["error_msgs"].append( - f"Failed to read {entrypoint_file}: {str(e)}" + f"Module {module_name} not found in {entrypoint_file}" + ) + validation_details["missing_files"].append( + f"{module_name} module reference" ) validation_details["valid"] = False - - except ValueError as e: - validation_details["error_msgs"].append(str(e)) - validation_details["missing_files"].append("valid runagent.config.json") - validation_details["valid"] = False + except Exception as e: + validation_details["error_msgs"].append( + f"Failed to read {entrypoint_file}: {str(e)}" + ) + validation_details["valid"] = False # Check for unwanted files for unwanted_file in unwanted_files: diff --git a/runagent/utils/enums.py b/runagent/utils/enums.py index 8603f12..956ed27 100644 --- a/runagent/utils/enums.py +++ b/runagent/utils/enums.py @@ -1,6 +1,26 @@ from enum import Enum +import typing as t class ResponseStatus(Enum): SUCCESS = "success" ERROR = "error" + + +class PythonicType(Enum): + AG2 = "ag2" + AGNO = "agno" + AUTOGEN = "autogen" + CREWAI = "crewai" + LANGCHAIN = "langchain" + LANGGRAPH = "langgraph" + LETTA = "letta" + LLAMAINDEX = "llamaindex" + OPENAI = "openai" + + +class WebhookType(Enum): + N8N = "n8n" + + +FrameworkType = t.Union[PythonicType, WebhookType] diff --git a/runagent/utils/schema.py b/runagent/utils/schema.py index dc9b9b2..972bda3 100644 --- a/runagent/utils/schema.py +++ b/runagent/utils/schema.py @@ -4,6 +4,7 @@ # from typing import Any, Dict, List, Optional, Union from enum import Enum from pydantic import BaseModel, Field, validator +from .enums import FrameworkType class TemplateSource(BaseModel): @@ -62,7 +63,7 @@ class RunAgentConfig(BaseModel): agent_name: str = Field(..., description="Name of the agent") description: str = Field(..., description="Description of the agent") - framework: str = Field(..., description="Framework used (langchain, etc)") + framework: FrameworkType = Field(..., description="Framework used (langchain, etc)") template: str = Field(..., description="Template name") version: str = Field(..., description="Agent version") created_at: datetime = Field(..., description="Creation timestamp") diff --git a/templates/n8n/default/requirements.txt b/templates/n8n/default/requirements.txt index ec838c5..e69de29 100644 --- a/templates/n8n/default/requirements.txt +++ b/templates/n8n/default/requirements.txt @@ -1 +0,0 @@ -openai diff --git a/test_scripts/python/client_test_n8n.py b/test_scripts/python/client_test_n8n.py index 9c55a51..6b63e9d 100644 --- a/test_scripts/python/client_test_n8n.py +++ b/test_scripts/python/client_test_n8n.py @@ -8,8 +8,7 @@ ) -agent_results = ra.run( - payload={ +agent_results = ra.run({ "recipe_name": "Chocolate Chip Pancakes", "servings": 4, "ingredients": [ @@ -26,18 +25,3 @@ ) pprint(agent_results) - -# ================================== - -# from runagent import RunAgentClient - -# ra = RunAgentClient( -# agent_id="40ca8515-a19a-49b2-8ef4-030f83bfc074", -# entrypoint_tag="simple_assistant_extracted_stream", -# local=True -# ) - -# for chunk in ra.run( -# user_msg="Analyze the benefits of remote work for software teams" -# ): -# print(chunk) From 97a4dccfea667be8c8a290b4271b4c0d128ec092 Mon Sep 17 00:00:00 2001 From: sawradip Date: Tue, 29 Jul 2025 14:22:30 +0600 Subject: [PATCH 08/16] feat: release script wip --- release.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/release.sh b/release.sh index 0bad715..eb69219 100644 --- a/release.sh +++ b/release.sh @@ -106,14 +106,14 @@ EOF check_prerequisites() { # Check if we're on main branch (strict requirement now) local current_branch=$(git branch --show-current) - if [[ "$current_branch" != "main" ]] && [[ "$current_branch" != "master" ]]; then - echo "❌ Error: You must be on the main branch to create a release" - echo "Current branch: $current_branch" - echo "" - echo "Please switch to main branch:" - echo " git checkout main" - exit 1 - fi + # if [[ "$current_branch" != "main" ]] && [[ "$current_branch" != "master" ]]; then + # echo "❌ Error: You must be on the main branch to create a release" + # echo "Current branch: $current_branch" + # echo "" + # echo "Please switch to main branch:" + # echo " git checkout main" + # exit 1 + # fi # Check for uncommitted changes if [[ -n $(git status --porcelain) ]]; then From aabb05533248a7aa8140bae198830ba8ac60dc8a Mon Sep 17 00:00:00 2001 From: sawradip Date: Tue, 29 Jul 2025 16:54:03 +0600 Subject: [PATCH 09/16] feat: fixing release.sh --- release.sh | 285 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 212 insertions(+), 73 deletions(-) diff --git a/release.sh b/release.sh index eb69219..be341e4 100644 --- a/release.sh +++ b/release.sh @@ -11,8 +11,8 @@ usage() { echo " 1. Update version in all SDK package files" echo " 2. Commit the changes" echo " 3. Create a git tag v" - echo " 4. Push everything to main branch" - echo " 5. Workflows will generate changelog and publish packages" + echo " 4. Push everything to current branch" + echo " 5. Workflows will generate changelog and publish packages when tag reaches main" echo "" echo "Example:" echo " $0 1.2.3" @@ -31,22 +31,41 @@ update_python_version() { local version=$1 local pyproject_file="pyproject.toml" local version_file="runagent/__version__.py" + local updated=false if [[ -f "$pyproject_file" ]]; then echo "πŸ“¦ Updating Python pyproject.toml version to $version" - sed -i.bak "s/version = \".*\"/version = \"$version\"/" "$pyproject_file" - rm "$pyproject_file.bak" 2>/dev/null || true + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + sed -i '' "s/version = \".*\"/version = \"$version\"/" "$pyproject_file" + else + # Linux + sed -i "s/version = \".*\"/version = \"$version\"/" "$pyproject_file" + fi + updated=true else echo "⚠️ Warning: $pyproject_file not found, skipping Python pyproject.toml update" fi if [[ -f "$version_file" ]]; then echo "πŸ“¦ Updating Python __version__.py to $version" - sed -i.bak "s/__version__ = \".*\"/__version__ = \"$version\"/" "$version_file" - rm "$version_file.bak" 2>/dev/null || true + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + sed -i '' "s/__version__ = \".*\"/__version__ = \"$version\"/" "$version_file" + else + # Linux + sed -i "s/__version__ = \".*\"/__version__ = \"$version\"/" "$version_file" + fi + updated=true else echo "πŸ“¦ Creating Python __version__.py with version $version" + mkdir -p "$(dirname "$version_file")" echo "__version__ = \"$version\"" > "$version_file" + updated=true + fi + + if [[ "$updated" == true ]]; then + echo "βœ… Python version updated" fi } @@ -54,13 +73,31 @@ update_javascript_version() { local version=$1 local file="runagent-ts/package.json" - if [[ -f "$file" ]]; then - echo "πŸ“¦ Updating JavaScript version to $version" - cd runagent-ts - npm version "$version" --no-git-tag-version --allow-same-version - cd .. - else + if [[ ! -f "$file" ]]; then echo "⚠️ Warning: $file not found, skipping JavaScript version update" + return + fi + + # Check if npm is available + if ! command -v npm &> /dev/null; then + echo "⚠️ Warning: npm not found, trying manual update of package.json" + + # Manual update using sed + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + sed -i '' "s/\"version\": \".*\"/\"version\": \"$version\"/" "$file" + else + # Linux + sed -i "s/\"version\": \".*\"/\"version\": \"$version\"/" "$file" + fi + echo "πŸ“¦ Manually updated JavaScript version to $version" + else + echo "πŸ“¦ Updating JavaScript version to $version" + ( + cd runagent-ts + npm version "$version" --no-git-tag-version --allow-same-version + ) + echo "βœ… JavaScript version updated" fi } @@ -70,8 +107,14 @@ update_rust_version() { if [[ -f "$file" ]]; then echo "πŸ“¦ Updating Rust version to $version" - sed -i.bak "s/^version = \".*\"/version = \"$version\"/" "$file" - rm "$file.bak" 2>/dev/null || true + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + sed -i '' "s/^version = \".*\"/version = \"$version\"/" "$file" + else + # Linux + sed -i "s/^version = \".*\"/version = \"$version\"/" "$file" + fi + echo "βœ… Rust version updated" else echo "⚠️ Warning: $file not found, skipping Rust version update" fi @@ -82,7 +125,7 @@ update_go_version() { local version_file="runagent-go/runagent/version.go" local go_mod_file="runagent-go/go.mod" - mkdir -p runagent-go/runagent + mkdir -p "$(dirname "$version_file")" echo "πŸ“¦ Updating Go version reference to $version" cat > "$version_file" << EOF package runagent @@ -93,6 +136,7 @@ EOF if [[ ! -f "$go_mod_file" ]]; then echo "πŸ“¦ Creating Go module file" + mkdir -p "$(dirname "$go_mod_file")" cat > "$go_mod_file" << EOF module github.com/runagent-dev/runagent/runagent-go @@ -101,37 +145,97 @@ go 1.20 // Dependencies will be added here EOF fi + echo "βœ… Go version updated" } check_prerequisites() { - # Check if we're on main branch (strict requirement now) - local current_branch=$(git branch --show-current) - # if [[ "$current_branch" != "main" ]] && [[ "$current_branch" != "master" ]]; then - # echo "❌ Error: You must be on the main branch to create a release" - # echo "Current branch: $current_branch" - # echo "" - # echo "Please switch to main branch:" - # echo " git checkout main" - # exit 1 - # fi + # Get current branch + local current_branch + current_branch=$(git branch --show-current 2>/dev/null || git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") + + if [[ "$current_branch" == "unknown" ]]; then + echo "❌ Error: Could not determine current branch" + exit 1 + fi + + echo "πŸ“ Current branch: $current_branch" + + # Warn if not on main, but don't force it + if [[ "$current_branch" != "main" ]] && [[ "$current_branch" != "master" ]]; then + echo "" + echo "⚠️ Warning: You're not on the main branch (current: $current_branch)" + echo " The release tag will be created on '$current_branch'" + echo " Note: Workflows only run when the tag is reachable from main branch" + echo "" + read -p "Continue with release from '$current_branch'? (y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Release cancelled. To release from main:" + echo " git checkout main" + echo " ./release.sh $VERSION" + exit 1 + fi + fi # Check for uncommitted changes - if [[ -n $(git status --porcelain) ]]; then + if [[ -n $(git status --porcelain 2>/dev/null) ]]; then echo "❌ Error: You have uncommitted changes" - git status --short + git status --short 2>/dev/null || echo "Could not show git status" echo "" echo "Please commit or stash your changes before releasing." exit 1 fi - # Check if required commands exist - if ! command -v npm &> /dev/null; then - echo "⚠️ Warning: npm not found, JavaScript version update may fail" + # Check if git is working + if ! git status &>/dev/null; then + echo "❌ Error: Not in a git repository or git is not working" + exit 1 + fi + + # Try to pull latest changes from current branch + echo "πŸ”„ Pulling latest changes from $current_branch..." + if git ls-remote --exit-code origin "$current_branch" &>/dev/null; then + # Remote branch exists, try to pull + if ! git pull origin "$current_branch"; then + echo "⚠️ Warning: Could not pull from origin/$current_branch" + echo " You might be ahead of remote or there might be conflicts" + read -p "Continue anyway? (y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + exit 1 + fi + fi + else + echo "⚠️ Remote branch origin/$current_branch does not exist" + echo " This might be a new branch that hasn't been pushed yet" + fi +} + +show_version_changes() { + echo "" + echo "πŸ” Version changes:" + echo "-------------------" + + # Show version changes + if [[ -f "pyproject.toml" ]]; then + echo "Python (pyproject.toml): $(grep 'version = ' pyproject.toml 2>/dev/null || echo 'not found')" + fi + + if [[ -f "runagent/__version__.py" ]]; then + echo "Python (__version__.py): $(grep '__version__ = ' runagent/__version__.py 2>/dev/null || echo 'not found')" + fi + + if [[ -f "runagent-ts/package.json" ]]; then + echo "JavaScript: $(grep '"version":' runagent-ts/package.json 2>/dev/null || echo 'not found')" fi - # Pull latest changes - echo "πŸ”„ Pulling latest changes from main..." - git pull origin main + if [[ -f "runagent-rust/runagent/Cargo.toml" ]]; then + echo "Rust: $(grep '^version = ' runagent-rust/runagent/Cargo.toml 2>/dev/null || echo 'not found')" + fi + + if [[ -f "runagent-go/runagent/version.go" ]]; then + echo "Go: $(grep 'const Version = ' runagent-go/runagent/version.go 2>/dev/null || echo 'not found')" + fi } # Main script @@ -157,61 +261,53 @@ check_prerequisites if git tag -l | grep -q "^v$VERSION$"; then echo "❌ Error: Tag v$VERSION already exists" echo "Existing tags:" - git tag -l | grep "^v" | sort -V | tail -5 + git tag -l | grep "^v" | sort -V | tail -5 2>/dev/null || echo "Could not list existing tags" exit 1 fi echo "" echo "πŸ“ Updating version files..." -# Update all package files +# Update all package files with error handling +set +e # Don't exit on errors for version updates update_python_version "$VERSION" update_javascript_version "$VERSION" update_rust_version "$VERSION" update_go_version "$VERSION" +set -e # Re-enable exit on error echo "" echo "πŸ“‹ Summary of changes:" -git diff --name-only - -echo "" -echo "πŸ” Version changes:" -echo "-------------------" - -# Show version changes -if [[ -f "pyproject.toml" ]]; then - echo "Python (pyproject.toml): $(grep 'version = ' pyproject.toml)" -fi - -if [[ -f "runagent/__version__.py" ]]; then - echo "Python (__version__.py): $(grep '__version__ = ' runagent/__version__.py)" +if git diff --name-only 2>/dev/null; then + echo "βœ… Changes detected" +else + echo "⚠️ No changes detected - this might be an issue" fi -if [[ -f "runagent-ts/package.json" ]]; then - echo "JavaScript: $(grep '"version":' runagent-ts/package.json)" -fi - -if [[ -f "runagent-rust/runagent/Cargo.toml" ]]; then - echo "Rust: $(grep '^version = ' runagent-rust/runagent/Cargo.toml)" -fi - -if [[ -f "runagent-go/runagent/version.go" ]]; then - echo "Go: $(grep 'const Version = ' runagent-go/runagent/version.go)" -fi +show_version_changes echo "" read -p "πŸ€” Do these changes look correct? (y/N): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo "❌ Release cancelled. Reverting changes..." - git checkout -- . + git checkout -- . 2>/dev/null || echo "Could not revert changes automatically" exit 1 fi echo "" echo "πŸ’Ύ Committing version changes..." +# Stage all changes git add . + +# Check if there are actually changes to commit +if git diff --staged --quiet; then + echo "⚠️ No changes to commit. This might indicate a problem with version updates." + exit 1 +fi + +# Commit changes git commit -m "chore: bump version to v$VERSION - Update Python SDK to v$VERSION @@ -234,25 +330,68 @@ All SDKs updated to version $VERSION: Changelog will be generated automatically by workflows." echo "" -echo "⬆️ Pushing to main branch with tag..." -git push origin main -git push origin "v$VERSION" +CURRENT_BRANCH=$(git branch --show-current 2>/dev/null || git rev-parse --abbrev-ref HEAD 2>/dev/null) +echo "⬆️ Pushing to $CURRENT_BRANCH with tag..." + +# Ask about tag pushing strategy if not on main +if [[ "$CURRENT_BRANCH" != "main" ]] && [[ "$CURRENT_BRANCH" != "master" ]]; then + echo "" + echo "πŸ€” Tag pushing strategy:" + echo " 1. Push tag now (workflows won't run until tag reaches main)" + echo " 2. Don't push tag yet (push manually after merging to main)" + echo "" + read -p "Choose option (1/2): " -n 1 -r + echo + + if [[ $REPLY == "2" ]]; then + # Push branch but not tag + if ! git push origin "$CURRENT_BRANCH"; then + echo "❌ Failed to push branch. You may need to set upstream:" + echo " git push -u origin $CURRENT_BRANCH" + exit 1 + fi + + echo "⏸️ Tag v$VERSION created locally but not pushed" + echo "" + echo "πŸ“‹ Next steps:" + echo " 1. Create PR and merge this branch to main" + echo " 2. After merging, run: git checkout main && git pull && git push origin v$VERSION" + echo " 3. This will trigger the release workflows" + echo "" + echo "🏷️ Tag: v$VERSION (local only, on $CURRENT_BRANCH)" + exit 0 + fi +fi + +# Push branch and tag +if ! git push origin "$CURRENT_BRANCH"; then + echo "❌ Failed to push branch. You may need to set upstream:" + echo " git push -u origin $CURRENT_BRANCH" + exit 1 +fi + +if ! git push origin "v$VERSION"; then + echo "❌ Failed to push tag" + exit 1 +fi echo "" -echo "βœ… Version v$VERSION tagged and pushed to main!" +echo "βœ… Version v$VERSION tagged and pushed!" echo "" echo "🎯 What happens next:" -echo " 1. GitHub Actions will detect the v$VERSION tag on main branch" -echo " 2. Unified release workflow will:" -echo " - Generate changelog from conventional commits" -echo " - Create main GitHub release with changelog" -echo " 3. Individual SDK workflows will:" -echo " - Test and publish to PyPI, npm, crates.io" -echo " - Update the main release with their status" +if [[ "$CURRENT_BRANCH" == "main" ]] || [[ "$CURRENT_BRANCH" == "master" ]]; then + echo " βœ… Tag is on main branch - workflows will run immediately:" + echo " - Generate changelog and create GitHub release" + echo " - Test and publish all SDK packages" +else + echo " ⏳ Tag is on '$CURRENT_BRANCH' - workflows will run when tag reaches main:" + echo " - Create PR to merge '$CURRENT_BRANCH' β†’ main" + echo " - After merge, workflows will detect tag and run automatically" +fi echo "" echo "πŸ“Š Monitor progress:" echo " - Actions: https://github.com/runagent-dev/runagent/actions" echo " - Releases: https://github.com/runagent-dev/runagent/releases" echo "" -echo "🏷️ Tag: v$VERSION (on main branch)" -echo "πŸ“ Changelog: Will be generated automatically by workflows" \ No newline at end of file +echo "🏷️ Tag: v$VERSION (on $CURRENT_BRANCH)" +echo "πŸ“ Changelog: Will be generated automatically when workflows run" \ No newline at end of file From a51e5dd27a73f0d1ed7233dd720901d44a4206b6 Mon Sep 17 00:00:00 2001 From: sawradip Date: Tue, 29 Jul 2025 16:55:09 +0600 Subject: [PATCH 10/16] feat: go version file added --- runagent-go/runagent/version.go | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 runagent-go/runagent/version.go diff --git a/runagent-go/runagent/version.go b/runagent-go/runagent/version.go new file mode 100644 index 0000000..55c6179 --- /dev/null +++ b/runagent-go/runagent/version.go @@ -0,0 +1,4 @@ +package runagent + +// Version represents the current version of the RunAgent Go SDK +const Version = "0.1.6" From 69ef2cec6d398193f6e924fd0cabd05cc4c78f1f Mon Sep 17 00:00:00 2001 From: sawradip Date: Tue, 29 Jul 2025 17:32:50 +0600 Subject: [PATCH 11/16] chore: bump version to v0.1.6 - Update Python SDK to v0.1.6 - Update JavaScript SDK to v0.1.6 - Update Rust SDK to v0.1.6 - Update Go SDK to v0.1.6 --- pyproject.toml | 6 +-- release.sh | 71 ++++++++++++++++--------------- runagent-rust/runagent/Cargo.toml | 2 +- runagent-ts/package-lock.json | 4 +- runagent-ts/package.json | 2 +- runagent/__version__.py | 2 +- 6 files changed, 45 insertions(+), 42 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 30ff45f..71c2faa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "hatchling.build" [project] name = "runagent" -version = "0.1.5" +version = "0.1.6" description = "A command-line tool and SDK for deploying, managing, and interacting with AI agents" readme = "README.md" requires-python = ">=3.9" @@ -103,7 +103,7 @@ line_length = 88 skip = ["docs"] [tool.mypy] -python_version = "3.9" +python_version = "0.1.6" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true @@ -159,7 +159,7 @@ fail_under = 80 [tool.ruff] line-length = 88 -target-version = "py39" +target-version = "0.1.6" select = [ "E", # pycodestyle errors "W", # pycodestyle warnings diff --git a/release.sh b/release.sh index be341e4..2bf011d 100644 --- a/release.sh +++ b/release.sh @@ -15,7 +15,7 @@ usage() { echo " 5. Workflows will generate changelog and publish packages when tag reaches main" echo "" echo "Example:" - echo " $0 1.2.3" + echo "bash ./release.sh 1.2.3" } validate_version() { @@ -37,6 +37,9 @@ update_python_version() { echo "πŸ“¦ Updating Python pyproject.toml version to $version" if [[ "$OSTYPE" == "darwin"* ]]; then # macOS + echo "Python Trial" + echo sed -i '' "s/version = \".*\"/version = \"$version\"/" "$pyproject_file" + sed -i '' "s/version = \".*\"/version = \"$version\"/" "$pyproject_file" else # Linux @@ -167,24 +170,24 @@ check_prerequisites() { echo " The release tag will be created on '$current_branch'" echo " Note: Workflows only run when the tag is reachable from main branch" echo "" - read -p "Continue with release from '$current_branch'? (y/N): " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo "Release cancelled. To release from main:" - echo " git checkout main" - echo " ./release.sh $VERSION" - exit 1 - fi + # read -p "Continue with release from '$current_branch'? (y/N): " -n 1 -r + # echo + # if [[ ! $REPLY =~ ^[Yy]$ ]]; then + # echo "Release cancelled. To release from main:" + # echo " git checkout main" + # echo " ./release.sh $VERSION" + # exit 1 + # fi fi - # Check for uncommitted changes - if [[ -n $(git status --porcelain 2>/dev/null) ]]; then - echo "❌ Error: You have uncommitted changes" - git status --short 2>/dev/null || echo "Could not show git status" - echo "" - echo "Please commit or stash your changes before releasing." - exit 1 - fi + # # Check for uncommitted changes + # if [[ -n $(git status --porcelain 2>/dev/null) ]]; then + # echo "❌ Error: You have uncommitted changes" + # git status --short 2>/dev/null || echo "Could not show git status" + # echo "" + # echo "Please commit or stash your changes before releasing." + # exit 1 + # fi # Check if git is working if ! git status &>/dev/null; then @@ -192,23 +195,23 @@ check_prerequisites() { exit 1 fi - # Try to pull latest changes from current branch - echo "πŸ”„ Pulling latest changes from $current_branch..." - if git ls-remote --exit-code origin "$current_branch" &>/dev/null; then - # Remote branch exists, try to pull - if ! git pull origin "$current_branch"; then - echo "⚠️ Warning: Could not pull from origin/$current_branch" - echo " You might be ahead of remote or there might be conflicts" - read -p "Continue anyway? (y/N): " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - exit 1 - fi - fi - else - echo "⚠️ Remote branch origin/$current_branch does not exist" - echo " This might be a new branch that hasn't been pushed yet" - fi + # # Try to pull latest changes from current branch + # echo "πŸ”„ Pulling latest changes from $current_branch..." + # if git ls-remote --exit-code origin "$current_branch" &>/dev/null; then + # # Remote branch exists, try to pull + # if ! git pull origin "$current_branch"; then + # echo "⚠️ Warning: Could not pull from origin/$current_branch" + # echo " You might be ahead of remote or there might be conflicts" + # read -p "Continue anyway? (y/N): " -n 1 -r + # echo + # if [[ ! $REPLY =~ ^[Yy]$ ]]; then + # exit 1 + # fi + # fi + # else + # echo "⚠️ Remote branch origin/$current_branch does not exist" + # echo " This might be a new branch that hasn't been pushed yet" + # fi } show_version_changes() { diff --git a/runagent-rust/runagent/Cargo.toml b/runagent-rust/runagent/Cargo.toml index 5e8fb77..750ca3d 100644 --- a/runagent-rust/runagent/Cargo.toml +++ b/runagent-rust/runagent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runagent" -version = "0.1.1" +version = "0.1.6" edition = "2021" description = "RunAgent SDK for Rust - Deploy and manage AI agents easily" license = "MIT" diff --git a/runagent-ts/package-lock.json b/runagent-ts/package-lock.json index 1688710..9adb264 100644 --- a/runagent-ts/package-lock.json +++ b/runagent-ts/package-lock.json @@ -1,12 +1,12 @@ { "name": "runagent", - "version": "0.1.4", + "version": "0.1.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "runagent", - "version": "0.1.4", + "version": "0.1.6", "dependencies": { "better-sqlite3": "^12.2.0" }, diff --git a/runagent-ts/package.json b/runagent-ts/package.json index 2de85d5..50797fb 100644 --- a/runagent-ts/package.json +++ b/runagent-ts/package.json @@ -1,6 +1,6 @@ { "name": "runagent", - "version": "0.1.4", + "version": "0.1.6", "type": "module", "files": [ "dist" diff --git a/runagent/__version__.py b/runagent/__version__.py index 3dc1f76..0a8da88 100644 --- a/runagent/__version__.py +++ b/runagent/__version__.py @@ -1 +1 @@ -__version__ = "0.1.0" +__version__ = "0.1.6" From ed082fac84fd793ee1dbc95054d8f8df783b4836 Mon Sep 17 00:00:00 2001 From: sawradip Date: Tue, 29 Jul 2025 17:41:06 +0600 Subject: [PATCH 12/16] chore: bump version to v0.1.7 - Update Python SDK to v0.1.7 - Update JavaScript SDK to v0.1.7 - Update Rust SDK to v0.1.7 - Update Go SDK to v0.1.7 --- pyproject.toml | 6 +- release.sh | 93 ++++++++++++++++--------------- runagent-go/runagent/version.go | 2 +- runagent-rust/runagent/Cargo.toml | 2 +- runagent-ts/package-lock.json | 4 +- runagent-ts/package.json | 2 +- runagent/__version__.py | 2 +- 7 files changed, 56 insertions(+), 55 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 71c2faa..3634df1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "hatchling.build" [project] name = "runagent" -version = "0.1.6" +version = "0.1.7" description = "A command-line tool and SDK for deploying, managing, and interacting with AI agents" readme = "README.md" requires-python = ">=3.9" @@ -103,7 +103,7 @@ line_length = 88 skip = ["docs"] [tool.mypy] -python_version = "0.1.6" +python_version = "0.1.7" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true @@ -159,7 +159,7 @@ fail_under = 80 [tool.ruff] line-length = 88 -target-version = "0.1.6" +target-version = "0.1.7" select = [ "E", # pycodestyle errors "W", # pycodestyle warnings diff --git a/release.sh b/release.sh index 2bf011d..77c494f 100644 --- a/release.sh +++ b/release.sh @@ -337,61 +337,62 @@ CURRENT_BRANCH=$(git branch --show-current 2>/dev/null || git rev-parse --abbrev echo "⬆️ Pushing to $CURRENT_BRANCH with tag..." # Ask about tag pushing strategy if not on main -if [[ "$CURRENT_BRANCH" != "main" ]] && [[ "$CURRENT_BRANCH" != "master" ]]; then - echo "" - echo "πŸ€” Tag pushing strategy:" - echo " 1. Push tag now (workflows won't run until tag reaches main)" - echo " 2. Don't push tag yet (push manually after merging to main)" - echo "" - read -p "Choose option (1/2): " -n 1 -r - echo +# if [[ "$CURRENT_BRANCH" != "main" ]] && [[ "$CURRENT_BRANCH" != "master" ]]; then + # echo "" + # echo "πŸ€” Tag pushing strategy:" + # echo " 1. Push tag now (workflows won't run until tag reaches main)" + # echo " 2. Don't push tag yet (push manually after merging to main)" + # echo "" + # read -p "Choose option (1/2): " -n 1 -r + # echo - if [[ $REPLY == "2" ]]; then - # Push branch but not tag - if ! git push origin "$CURRENT_BRANCH"; then - echo "❌ Failed to push branch. You may need to set upstream:" - echo " git push -u origin $CURRENT_BRANCH" - exit 1 - fi - - echo "⏸️ Tag v$VERSION created locally but not pushed" - echo "" - echo "πŸ“‹ Next steps:" - echo " 1. Create PR and merge this branch to main" - echo " 2. After merging, run: git checkout main && git pull && git push origin v$VERSION" - echo " 3. This will trigger the release workflows" - echo "" - echo "🏷️ Tag: v$VERSION (local only, on $CURRENT_BRANCH)" - exit 0 - fi -fi - -# Push branch and tag -if ! git push origin "$CURRENT_BRANCH"; then - echo "❌ Failed to push branch. You may need to set upstream:" - echo " git push -u origin $CURRENT_BRANCH" - exit 1 -fi - -if ! git push origin "v$VERSION"; then - echo "❌ Failed to push tag" - exit 1 -fi + # if [[ $REPLY == "1" ]]; then + # # Push branch but not tag + # if ! git push origin "$CURRENT_BRANCH"; then + # echo "❌ Failed to push branch. You may need to set upstream:" + # echo " git push -u origin $CURRENT_BRANCH" + # exit 1 + # fi + +echo "⏸️ Tag v$VERSION created locally but not pushed" +echo "" +echo "πŸ“‹ Next steps:" +echo " 1. Push changes to remote branch" +echo " 2. Create PR and merge this branch to main" +echo " 3. After merging, run: git checkout main && git pull && git push origin v$VERSION" +echo " 4. This will trigger the release workflows" +echo "" +echo "🏷️ Tag: v$VERSION (local only, on $CURRENT_BRANCH)" +# exit 0 +# fi +# fi + +# # Push branch and tag +# if ! git push origin "$CURRENT_BRANCH"; then +# echo "❌ Failed to push branch. You may need to set upstream:" +# echo " git push -u origin $CURRENT_BRANCH" +# exit 1 +# fi + +# if ! git push origin "v$VERSION"; then +# echo "❌ Failed to push tag" +# exit 1 +# fi echo "" -echo "βœ… Version v$VERSION tagged and pushed!" +echo "βœ… Version v$VERSION tagged!" echo "" echo "🎯 What happens next:" if [[ "$CURRENT_BRANCH" == "main" ]] || [[ "$CURRENT_BRANCH" == "master" ]]; then echo " βœ… Tag is on main branch - workflows will run immediately:" echo " - Generate changelog and create GitHub release" echo " - Test and publish all SDK packages" -else - echo " ⏳ Tag is on '$CURRENT_BRANCH' - workflows will run when tag reaches main:" - echo " - Create PR to merge '$CURRENT_BRANCH' β†’ main" - echo " - After merge, workflows will detect tag and run automatically" -fi -echo "" +# else +# echo " ⏳ Tag is on '$CURRENT_BRANCH' - workflows will run when tag reaches main:" +# echo " - Create PR to merge '$CURRENT_BRANCH' β†’ main" +# echo " - After merge, workflows will detect tag and run automatically" +# fi +# echo "" echo "πŸ“Š Monitor progress:" echo " - Actions: https://github.com/runagent-dev/runagent/actions" echo " - Releases: https://github.com/runagent-dev/runagent/releases" diff --git a/runagent-go/runagent/version.go b/runagent-go/runagent/version.go index 55c6179..6ef68e3 100644 --- a/runagent-go/runagent/version.go +++ b/runagent-go/runagent/version.go @@ -1,4 +1,4 @@ package runagent // Version represents the current version of the RunAgent Go SDK -const Version = "0.1.6" +const Version = "0.1.7" diff --git a/runagent-rust/runagent/Cargo.toml b/runagent-rust/runagent/Cargo.toml index 750ca3d..4518723 100644 --- a/runagent-rust/runagent/Cargo.toml +++ b/runagent-rust/runagent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runagent" -version = "0.1.6" +version = "0.1.7" edition = "2021" description = "RunAgent SDK for Rust - Deploy and manage AI agents easily" license = "MIT" diff --git a/runagent-ts/package-lock.json b/runagent-ts/package-lock.json index 9adb264..beb3b46 100644 --- a/runagent-ts/package-lock.json +++ b/runagent-ts/package-lock.json @@ -1,12 +1,12 @@ { "name": "runagent", - "version": "0.1.6", + "version": "0.1.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "runagent", - "version": "0.1.6", + "version": "0.1.7", "dependencies": { "better-sqlite3": "^12.2.0" }, diff --git a/runagent-ts/package.json b/runagent-ts/package.json index 50797fb..40b9cf7 100644 --- a/runagent-ts/package.json +++ b/runagent-ts/package.json @@ -1,6 +1,6 @@ { "name": "runagent", - "version": "0.1.6", + "version": "0.1.7", "type": "module", "files": [ "dist" diff --git a/runagent/__version__.py b/runagent/__version__.py index 0a8da88..f1380ee 100644 --- a/runagent/__version__.py +++ b/runagent/__version__.py @@ -1 +1 @@ -__version__ = "0.1.6" +__version__ = "0.1.7" From fc2608a5ac33faa6eee5fe5ba91e36c9303695de Mon Sep 17 00:00:00 2001 From: sawradip Date: Tue, 29 Jul 2025 18:00:16 +0600 Subject: [PATCH 13/16] feat: release script working --- release.sh | 388 +++++++++++++++++++++-------------------------------- 1 file changed, 155 insertions(+), 233 deletions(-) diff --git a/release.sh b/release.sh index 77c494f..aa750bb 100644 --- a/release.sh +++ b/release.sh @@ -3,6 +3,12 @@ set -e +# Status tracking (compatible with older bash) +python_status=false +javascript_status=false +rust_status=false +go_status=false + usage() { echo "Usage: $0 " echo "Version: semantic version (e.g., 1.2.3)" @@ -12,7 +18,6 @@ usage() { echo " 2. Commit the changes" echo " 3. Create a git tag v" echo " 4. Push everything to current branch" - echo " 5. Workflows will generate changelog and publish packages when tag reaches main" echo "" echo "Example:" echo "bash ./release.sh 1.2.3" @@ -27,49 +32,53 @@ validate_version() { return 0 } +verify_version_update() { + local file=$1 + local version=$2 + local pattern=$3 + + if [[ -f "$file" ]] && grep -q "$pattern" "$file" 2>/dev/null; then + return 0 + fi + return 1 +} + update_python_version() { local version=$1 local pyproject_file="pyproject.toml" local version_file="runagent/__version__.py" - local updated=false + local success=false + # Update pyproject.toml if [[ -f "$pyproject_file" ]]; then - echo "πŸ“¦ Updating Python pyproject.toml version to $version" if [[ "$OSTYPE" == "darwin"* ]]; then - # macOS - echo "Python Trial" - echo sed -i '' "s/version = \".*\"/version = \"$version\"/" "$pyproject_file" - sed -i '' "s/version = \".*\"/version = \"$version\"/" "$pyproject_file" else - # Linux sed -i "s/version = \".*\"/version = \"$version\"/" "$pyproject_file" fi - updated=true - else - echo "⚠️ Warning: $pyproject_file not found, skipping Python pyproject.toml update" + + if verify_version_update "$pyproject_file" "$version" "version = \"$version\""; then + success=true + fi fi + # Update __version__.py if [[ -f "$version_file" ]]; then - echo "πŸ“¦ Updating Python __version__.py to $version" if [[ "$OSTYPE" == "darwin"* ]]; then - # macOS sed -i '' "s/__version__ = \".*\"/__version__ = \"$version\"/" "$version_file" else - # Linux sed -i "s/__version__ = \".*\"/__version__ = \"$version\"/" "$version_file" fi - updated=true else - echo "πŸ“¦ Creating Python __version__.py with version $version" mkdir -p "$(dirname "$version_file")" echo "__version__ = \"$version\"" > "$version_file" - updated=true fi - if [[ "$updated" == true ]]; then - echo "βœ… Python version updated" + if verify_version_update "$version_file" "$version" "__version__ = \"$version\""; then + success=true fi + + python_status=$success } update_javascript_version() { @@ -77,30 +86,27 @@ update_javascript_version() { local file="runagent-ts/package.json" if [[ ! -f "$file" ]]; then - echo "⚠️ Warning: $file not found, skipping JavaScript version update" + javascript_status=false return fi - # Check if npm is available - if ! command -v npm &> /dev/null; then - echo "⚠️ Warning: npm not found, trying manual update of package.json" - - # Manual update using sed + if command -v npm &> /dev/null; then + ( + cd runagent-ts + npm version "$version" --no-git-tag-version --allow-same-version &>/dev/null + ) + else if [[ "$OSTYPE" == "darwin"* ]]; then - # macOS sed -i '' "s/\"version\": \".*\"/\"version\": \"$version\"/" "$file" else - # Linux sed -i "s/\"version\": \".*\"/\"version\": \"$version\"/" "$file" fi - echo "πŸ“¦ Manually updated JavaScript version to $version" + fi + + if verify_version_update "$file" "$version" "\"version\": \"$version\""; then + javascript_status=true else - echo "πŸ“¦ Updating JavaScript version to $version" - ( - cd runagent-ts - npm version "$version" --no-git-tag-version --allow-same-version - ) - echo "βœ… JavaScript version updated" + javascript_status=false fi } @@ -108,18 +114,21 @@ update_rust_version() { local version=$1 local file="runagent-rust/runagent/Cargo.toml" - if [[ -f "$file" ]]; then - echo "πŸ“¦ Updating Rust version to $version" - if [[ "$OSTYPE" == "darwin"* ]]; then - # macOS - sed -i '' "s/^version = \".*\"/version = \"$version\"/" "$file" - else - # Linux - sed -i "s/^version = \".*\"/version = \"$version\"/" "$file" - fi - echo "βœ… Rust version updated" + if [[ ! -f "$file" ]]; then + rust_status=false + return + fi + + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' "s/^version = \".*\"/version = \"$version\"/" "$file" + else + sed -i "s/^version = \".*\"/version = \"$version\"/" "$file" + fi + + if verify_version_update "$file" "$version" "version = \"$version\""; then + rust_status=true else - echo "⚠️ Warning: $file not found, skipping Rust version update" + rust_status=false fi } @@ -129,7 +138,6 @@ update_go_version() { local go_mod_file="runagent-go/go.mod" mkdir -p "$(dirname "$version_file")" - echo "πŸ“¦ Updating Go version reference to $version" cat > "$version_file" << EOF package runagent @@ -138,7 +146,6 @@ const Version = "$version" EOF if [[ ! -f "$go_mod_file" ]]; then - echo "πŸ“¦ Creating Go module file" mkdir -p "$(dirname "$go_mod_file")" cat > "$go_mod_file" << EOF module github.com/runagent-dev/runagent/runagent-go @@ -148,11 +155,46 @@ go 1.20 // Dependencies will be added here EOF fi - echo "βœ… Go version updated" + + if verify_version_update "$version_file" "$version" "const Version = \"$version\""; then + go_status=true + else + go_status=false + fi +} + +show_update_summary() { + echo "" + echo "πŸ“‹ Update Summary:" + echo "-------------------" + + if [[ "$python_status" == "true" ]]; then + echo "βœ… python" + else + echo "❌ python" + fi + + if [[ "$javascript_status" == "true" ]]; then + echo "βœ… javascript" + else + echo "❌ javascript" + fi + + if [[ "$rust_status" == "true" ]]; then + echo "βœ… rust" + else + echo "❌ rust" + fi + + if [[ "$go_status" == "true" ]]; then + echo "βœ… go" + else + echo "❌ go" + fi + echo "" } check_prerequisites() { - # Get current branch local current_branch current_branch=$(git branch --show-current 2>/dev/null || git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") @@ -161,84 +203,41 @@ check_prerequisites() { exit 1 fi - echo "πŸ“ Current branch: $current_branch" - - # Warn if not on main, but don't force it - if [[ "$current_branch" != "main" ]] && [[ "$current_branch" != "master" ]]; then - echo "" - echo "⚠️ Warning: You're not on the main branch (current: $current_branch)" - echo " The release tag will be created on '$current_branch'" - echo " Note: Workflows only run when the tag is reachable from main branch" - echo "" - # read -p "Continue with release from '$current_branch'? (y/N): " -n 1 -r - # echo - # if [[ ! $REPLY =~ ^[Yy]$ ]]; then - # echo "Release cancelled. To release from main:" - # echo " git checkout main" - # echo " ./release.sh $VERSION" - # exit 1 - # fi - fi - - # # Check for uncommitted changes - # if [[ -n $(git status --porcelain 2>/dev/null) ]]; then - # echo "❌ Error: You have uncommitted changes" - # git status --short 2>/dev/null || echo "Could not show git status" - # echo "" - # echo "Please commit or stash your changes before releasing." - # exit 1 - # fi - - # Check if git is working if ! git status &>/dev/null; then echo "❌ Error: Not in a git repository or git is not working" exit 1 fi - - # # Try to pull latest changes from current branch - # echo "πŸ”„ Pulling latest changes from $current_branch..." - # if git ls-remote --exit-code origin "$current_branch" &>/dev/null; then - # # Remote branch exists, try to pull - # if ! git pull origin "$current_branch"; then - # echo "⚠️ Warning: Could not pull from origin/$current_branch" - # echo " You might be ahead of remote or there might be conflicts" - # read -p "Continue anyway? (y/N): " -n 1 -r - # echo - # if [[ ! $REPLY =~ ^[Yy]$ ]]; then - # exit 1 - # fi - # fi - # else - # echo "⚠️ Remote branch origin/$current_branch does not exist" - # echo " This might be a new branch that hasn't been pushed yet" - # fi } -show_version_changes() { - echo "" - echo "πŸ” Version changes:" - echo "-------------------" - - # Show version changes - if [[ -f "pyproject.toml" ]]; then - echo "Python (pyproject.toml): $(grep 'version = ' pyproject.toml 2>/dev/null || echo 'not found')" - fi - - if [[ -f "runagent/__version__.py" ]]; then - echo "Python (__version__.py): $(grep '__version__ = ' runagent/__version__.py 2>/dev/null || echo 'not found')" - fi - - if [[ -f "runagent-ts/package.json" ]]; then - echo "JavaScript: $(grep '"version":' runagent-ts/package.json 2>/dev/null || echo 'not found')" - fi +handle_existing_tag() { + local version=$1 + local tag_name="v$version" + + if git tag -l | grep -q "^$tag_name$"; then + echo "" + echo "⚠️ Tag $tag_name already exists" + echo "" + echo "Do you want to move this tag to the current commit?" + echo "This will update the existing tag (potentially dangerous if already published)" + echo "" + read -p "Move tag to current commit? [y/N]: " -r + echo "" + + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Release cancelled. Tag $tag_name remains unchanged." + exit 0 + fi + + echo "Moving tag $tag_name to current commit..." + git tag -f -a "$tag_name" -m "Release $tag_name (moved) - if [[ -f "runagent-rust/runagent/Cargo.toml" ]]; then - echo "Rust: $(grep '^version = ' runagent-rust/runagent/Cargo.toml 2>/dev/null || echo 'not found')" - fi +RunAgent Universal Release $tag_name - if [[ -f "runagent-go/runagent/version.go" ]]; then - echo "Go: $(grep 'const Version = ' runagent-go/runagent/version.go 2>/dev/null || echo 'not found')" +All SDKs updated to version $version" + return 0 fi + + return 1 } # Main script @@ -249,153 +248,76 @@ fi VERSION=$1 -# Validate version format if ! validate_version "$VERSION"; then exit 1 fi echo "πŸš€ RunAgent Version Bump to v$VERSION" -echo "====================================" -# Run prerequisite checks check_prerequisites -# Check if tag already exists -if git tag -l | grep -q "^v$VERSION$"; then - echo "❌ Error: Tag v$VERSION already exists" - echo "Existing tags:" - git tag -l | grep "^v" | sort -V | tail -5 2>/dev/null || echo "Could not list existing tags" - exit 1 -fi - -echo "" -echo "πŸ“ Updating version files..." - -# Update all package files with error handling -set +e # Don't exit on errors for version updates +# Update all package files update_python_version "$VERSION" update_javascript_version "$VERSION" update_rust_version "$VERSION" update_go_version "$VERSION" -set -e # Re-enable exit on error -echo "" -echo "πŸ“‹ Summary of changes:" -if git diff --name-only 2>/dev/null; then - echo "βœ… Changes detected" -else - echo "⚠️ No changes detected - this might be an issue" +show_update_summary + +# Check if any updates succeeded +any_success=false +if [[ "$python_status" == "true" ]] || [[ "$javascript_status" == "true" ]] || [[ "$rust_status" == "true" ]] || [[ "$go_status" == "true" ]]; then + any_success=true fi -show_version_changes +if [[ "$any_success" == "false" ]]; then + echo "❌ No version updates succeeded. Aborting release." + exit 1 +fi + +# Show git changes +if ! git diff --name-only --quiet 2>/dev/null; then + echo "Changes detected:" + git diff --name-only 2>/dev/null | sed 's/^/ /' +else + echo "⚠️ No git changes detected" +fi echo "" -read -p "πŸ€” Do these changes look correct? (y/N): " -n 1 -r -echo +read -p "Continue with commit and tag? [y/N]: " -r +echo "" if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo "❌ Release cancelled. Reverting changes..." - git checkout -- . 2>/dev/null || echo "Could not revert changes automatically" - exit 1 + echo "Release cancelled." + git checkout -- . 2>/dev/null || true + exit 0 fi -echo "" -echo "πŸ’Ύ Committing version changes..." +# Handle existing tag +if handle_existing_tag "$VERSION"; then + echo "βœ… Tag v$VERSION updated successfully!" + exit 0 +fi -# Stage all changes +# Stage and commit changes git add . -# Check if there are actually changes to commit if git diff --staged --quiet; then - echo "⚠️ No changes to commit. This might indicate a problem with version updates." + echo "⚠️ No changes to commit." exit 1 fi -# Commit changes -git commit -m "chore: bump version to v$VERSION +git commit -m "chore: bump version to v$VERSION" -q -- Update Python SDK to v$VERSION -- Update JavaScript SDK to v$VERSION -- Update Rust SDK to v$VERSION -- Update Go SDK to v$VERSION" - -echo "" -echo "🏷️ Creating tag v$VERSION..." +# Create new tag git tag -a "v$VERSION" -m "Release v$VERSION -RunAgent Universal Release v$VERSION +RunAgent Universal Release v$VERSION" -All SDKs updated to version $VERSION: -- Python SDK: v$VERSION -- JavaScript SDK: v$VERSION -- Rust SDK: v$VERSION -- Go SDK: v$VERSION +echo "βœ… Tag v$VERSION created successfully!" -Changelog will be generated automatically by workflows." - -echo "" CURRENT_BRANCH=$(git branch --show-current 2>/dev/null || git rev-parse --abbrev-ref HEAD 2>/dev/null) -echo "⬆️ Pushing to $CURRENT_BRANCH with tag..." - -# Ask about tag pushing strategy if not on main -# if [[ "$CURRENT_BRANCH" != "main" ]] && [[ "$CURRENT_BRANCH" != "master" ]]; then - # echo "" - # echo "πŸ€” Tag pushing strategy:" - # echo " 1. Push tag now (workflows won't run until tag reaches main)" - # echo " 2. Don't push tag yet (push manually after merging to main)" - # echo "" - # read -p "Choose option (1/2): " -n 1 -r - # echo - - # if [[ $REPLY == "1" ]]; then - # # Push branch but not tag - # if ! git push origin "$CURRENT_BRANCH"; then - # echo "❌ Failed to push branch. You may need to set upstream:" - # echo " git push -u origin $CURRENT_BRANCH" - # exit 1 - # fi - -echo "⏸️ Tag v$VERSION created locally but not pushed" echo "" echo "πŸ“‹ Next steps:" -echo " 1. Push changes to remote branch" -echo " 2. Create PR and merge this branch to main" -echo " 3. After merging, run: git checkout main && git pull && git push origin v$VERSION" -echo " 4. This will trigger the release workflows" -echo "" -echo "🏷️ Tag: v$VERSION (local only, on $CURRENT_BRANCH)" -# exit 0 -# fi -# fi - -# # Push branch and tag -# if ! git push origin "$CURRENT_BRANCH"; then -# echo "❌ Failed to push branch. You may need to set upstream:" -# echo " git push -u origin $CURRENT_BRANCH" -# exit 1 -# fi - -# if ! git push origin "v$VERSION"; then -# echo "❌ Failed to push tag" -# exit 1 -# fi - -echo "" -echo "βœ… Version v$VERSION tagged!" -echo "" -echo "🎯 What happens next:" -if [[ "$CURRENT_BRANCH" == "main" ]] || [[ "$CURRENT_BRANCH" == "master" ]]; then - echo " βœ… Tag is on main branch - workflows will run immediately:" - echo " - Generate changelog and create GitHub release" - echo " - Test and publish all SDK packages" -# else -# echo " ⏳ Tag is on '$CURRENT_BRANCH' - workflows will run when tag reaches main:" -# echo " - Create PR to merge '$CURRENT_BRANCH' β†’ main" -# echo " - After merge, workflows will detect tag and run automatically" -# fi -# echo "" -echo "πŸ“Š Monitor progress:" -echo " - Actions: https://github.com/runagent-dev/runagent/actions" -echo " - Releases: https://github.com/runagent-dev/runagent/releases" -echo "" -echo "🏷️ Tag: v$VERSION (on $CURRENT_BRANCH)" -echo "πŸ“ Changelog: Will be generated automatically when workflows run" \ No newline at end of file +echo " 1. Push changes: git push origin $CURRENT_BRANCH" +echo " 2. Push tag: git push origin v$VERSION" +echo " 3. Monitor workflows at: https://github.com/runagent-dev/runagent/actions" \ No newline at end of file From c9e858134025c50d55f051db28e7b1798d8e4641 Mon Sep 17 00:00:00 2001 From: sawradip Date: Tue, 29 Jul 2025 18:01:17 +0600 Subject: [PATCH 14/16] chore: bump version to v0.1.8 --- pyproject.toml | 6 +++--- runagent-go/runagent/version.go | 2 +- runagent-rust/runagent/Cargo.toml | 2 +- runagent-ts/package-lock.json | 4 ++-- runagent-ts/package.json | 2 +- runagent/__version__.py | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3634df1..a994ccd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "hatchling.build" [project] name = "runagent" -version = "0.1.7" +version = "0.1.8" description = "A command-line tool and SDK for deploying, managing, and interacting with AI agents" readme = "README.md" requires-python = ">=3.9" @@ -103,7 +103,7 @@ line_length = 88 skip = ["docs"] [tool.mypy] -python_version = "0.1.7" +python_version = "0.1.8" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true @@ -159,7 +159,7 @@ fail_under = 80 [tool.ruff] line-length = 88 -target-version = "0.1.7" +target-version = "0.1.8" select = [ "E", # pycodestyle errors "W", # pycodestyle warnings diff --git a/runagent-go/runagent/version.go b/runagent-go/runagent/version.go index 6ef68e3..5180553 100644 --- a/runagent-go/runagent/version.go +++ b/runagent-go/runagent/version.go @@ -1,4 +1,4 @@ package runagent // Version represents the current version of the RunAgent Go SDK -const Version = "0.1.7" +const Version = "0.1.8" diff --git a/runagent-rust/runagent/Cargo.toml b/runagent-rust/runagent/Cargo.toml index 4518723..40c42fd 100644 --- a/runagent-rust/runagent/Cargo.toml +++ b/runagent-rust/runagent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runagent" -version = "0.1.7" +version = "0.1.8" edition = "2021" description = "RunAgent SDK for Rust - Deploy and manage AI agents easily" license = "MIT" diff --git a/runagent-ts/package-lock.json b/runagent-ts/package-lock.json index beb3b46..a724aef 100644 --- a/runagent-ts/package-lock.json +++ b/runagent-ts/package-lock.json @@ -1,12 +1,12 @@ { "name": "runagent", - "version": "0.1.7", + "version": "0.1.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "runagent", - "version": "0.1.7", + "version": "0.1.8", "dependencies": { "better-sqlite3": "^12.2.0" }, diff --git a/runagent-ts/package.json b/runagent-ts/package.json index 40b9cf7..40110fb 100644 --- a/runagent-ts/package.json +++ b/runagent-ts/package.json @@ -1,6 +1,6 @@ { "name": "runagent", - "version": "0.1.7", + "version": "0.1.8", "type": "module", "files": [ "dist" diff --git a/runagent/__version__.py b/runagent/__version__.py index f1380ee..9cb17e7 100644 --- a/runagent/__version__.py +++ b/runagent/__version__.py @@ -1 +1 @@ -__version__ = "0.1.7" +__version__ = "0.1.8" From 317fbffff1fc7dc68eb0d3a04c6a1e0c8d0a520c Mon Sep 17 00:00:00 2001 From: sawradip Date: Tue, 29 Jul 2025 18:16:15 +0600 Subject: [PATCH 15/16] feat: added create_release workflow --- .github/workflows/create-release.yml | 258 ++++++++++++++++++++------- 1 file changed, 191 insertions(+), 67 deletions(-) diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index c69523d..0afce07 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -3,91 +3,215 @@ name: Create GitHub Release on: push: - tags: - - 'v*' # Triggers on tags like v1.2.3 + branches: [main] # Trigger on ANY push to main (including PR merges) + pull_request: + types: [closed] # Also trigger when PRs are merged + branches: [main] jobs: - create-main-release: + detect-and-release: runs-on: ubuntu-latest + if: github.event_name == 'push' || (github.event.pull_request.merged == true) steps: - uses: actions/checkout@v4 with: - fetch-depth: 0 # Need full history for changelog + fetch-depth: 0 # Need full history to find tags - - name: Extract version from tag - id: extract_version + - name: Find new tags reachable from main + id: find-tags run: | - VERSION=${GITHUB_REF#refs/tags/v} - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "tag_name=v$VERSION" >> $GITHUB_OUTPUT - echo "Extracted version: $VERSION" + echo "πŸ” [MAIN RELEASE] Checking for version tags now reachable from main..." + + # Get all version tags + ALL_TAGS=$(git tag -l 'v*' | sort -V) + echo "All version tags: $ALL_TAGS" + + # Find tags that are reachable from main + REACHABLE_TAGS="" + for tag in $ALL_TAGS; do + TAG_COMMIT=$(git rev-list -n 1 $tag) + if git merge-base --is-ancestor $TAG_COMMIT HEAD; then + echo "βœ… Tag $tag is reachable from main (commit: $TAG_COMMIT)" + REACHABLE_TAGS="$REACHABLE_TAGS $tag" + else + echo "❌ Tag $tag is NOT reachable from main (commit: $TAG_COMMIT)" + fi + done + + echo "reachable_tags=$REACHABLE_TAGS" >> $GITHUB_OUTPUT + + # Find the latest reachable tag + if [ -n "$REACHABLE_TAGS" ]; then + LATEST_TAG=$(echo $REACHABLE_TAGS | tr ' ' '\n' | sort -V | tail -1) + echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT + echo "🏷️ Latest reachable tag: $LATEST_TAG" + + # Extract version + VERSION=${LATEST_TAG#v} + echo "version=$VERSION" >> $GITHUB_OUTPUT + else + echo "No version tags reachable from main" + echo "latest_tag=" >> $GITHUB_OUTPUT + echo "version=" >> $GITHUB_OUTPUT + fi - - name: Extract changelog for this version + - name: Check if this is a new release + id: check-release + run: | + LATEST_TAG="${{ steps.find-tags.outputs.latest_tag }}" + + if [ -z "$LATEST_TAG" ]; then + echo "No tags found, skipping release" + echo "should_release=false" >> $GITHUB_OUTPUT + exit 0 + fi + + # Check if we already created a release for this tag + if gh release view "$LATEST_TAG" >/dev/null 2>&1; then + echo "Release for $LATEST_TAG already exists, skipping" + echo "should_release=false" >> $GITHUB_OUTPUT + else + echo "πŸš€ New tag $LATEST_TAG detected, should create release" + echo "should_release=true" >> $GITHUB_OUTPUT + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Install git-cliff for changelog generation + if: steps.check-release.outputs.should_release == 'true' + run: | + echo "πŸ“₯ Installing git-cliff for changelog generation..." + curl -L https://github.com/orhun/git-cliff/releases/latest/download/git-cliff-x86_64-unknown-linux-gnu.tar.gz | tar -xz + sudo mv git-cliff-*/git-cliff /usr/local/bin/ + git-cliff --version + + - name: Generate changelog + if: steps.check-release.outputs.should_release == 'true' id: changelog run: | - if [ -f "CHANGELOG.md" ]; then - echo "πŸ“ Extracting changelog from committed CHANGELOG.md" - - # Extract the section for this version from CHANGELOG.md - # This assumes the changelog was generated locally and committed - awk '/^## \[.*\].*/{if(found) exit; if(/\['${{ steps.extract_version.outputs.version }}'\]/) found=1; if(found) print; next} found' CHANGELOG.md > current_changelog.md - - # If the extraction didn't work well, try a different approach - if [ ! -s current_changelog.md ]; then - # Extract everything after the first ## heading until the next ## - sed -n '/^## \[/,/^## \[/p' CHANGELOG.md | head -n -1 > current_changelog.md - fi - - # If still empty, use the first 30 lines of changelog - if [ ! -s current_changelog.md ]; then - head -30 CHANGELOG.md > current_changelog.md - fi - - # Read the changelog content - CHANGELOG_CONTENT=$(cat current_changelog.md) - - # Clean up and format for GitHub - echo "changelog<> $GITHUB_OUTPUT - echo "$CHANGELOG_CONTENT" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - echo "βœ… Changelog extracted successfully" - echo "Preview:" - echo "--------" - head -10 current_changelog.md - + echo "πŸ“ Generating changelog for v${{ steps.find-tags.outputs.version }}..." + + # Find the last tag to show range + LAST_TAG=$(git describe --tags --abbrev=0 ${{ steps.find-tags.outputs.latest_tag }}^ 2>/dev/null || echo "") + + if [ -n "$LAST_TAG" ]; then + echo "πŸ“ Generating changelog for range: $LAST_TAG β†’ ${{ steps.find-tags.outputs.latest_tag }}" + COMMIT_COUNT=$(git rev-list --count $LAST_TAG..${{ steps.find-tags.outputs.latest_tag }}) + echo "πŸ“Š Found $COMMIT_COUNT commits since $LAST_TAG" else - echo "⚠️ No CHANGELOG.md found, generating basic changelog" - - # Fallback: generate basic changelog from git history - LAST_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "") + echo "πŸ“ No previous tags found - generating changelog for initial release" + COMMIT_COUNT=$(git rev-list --count ${{ steps.find-tags.outputs.latest_tag }}) + echo "πŸ“Š Found $COMMIT_COUNT total commits" + fi + + # Generate changelog using git-cliff + git-cliff --tag "${{ steps.find-tags.outputs.latest_tag }}" --strip header --strip footer > current_changelog.md + + # If git-cliff output is empty or too short, create a basic changelog + if [ ! -s current_changelog.md ] || [ $(wc -l < current_changelog.md) -lt 3 ]; then + echo "⚠️ Git-cliff output was minimal, creating basic changelog" if [ -n "$LAST_TAG" ]; then echo "## What's Changed" > current_changelog.md echo "" >> current_changelog.md - git log --pretty=format:"- %s" $LAST_TAG..HEAD >> current_changelog.md + + # Categorize commits + echo "### ✨ Features" >> current_changelog.md + git log --pretty=format:"- %s" $LAST_TAG..${{ steps.find-tags.outputs.latest_tag }} | grep -i "^- feat" | sed 's/^- feat[:(]/- /' | sed 's/^- feat: /- /' >> current_changelog.md || echo "- No new features" >> current_changelog.md + echo "" >> current_changelog.md + + echo "### πŸ› Bug Fixes" >> current_changelog.md + git log --pretty=format:"- %s" $LAST_TAG..${{ steps.find-tags.outputs.latest_tag }} | grep -i "^- fix" | sed 's/^- fix[:(]/- /' | sed 's/^- fix: /- /' >> current_changelog.md || echo "- No bug fixes" >> current_changelog.md + echo "" >> current_changelog.md + + echo "### πŸ“š Documentation" >> current_changelog.md + git log --pretty=format:"- %s" $LAST_TAG..${{ steps.find-tags.outputs.latest_tag }} | grep -i "^- docs" | sed 's/^- docs[:(]/- /' | sed 's/^- docs: /- /' >> current_changelog.md || echo "- No documentation changes" >> current_changelog.md + echo "" >> current_changelog.md + + echo "### Other Changes" >> current_changelog.md + git log --pretty=format:"- %s" $LAST_TAG..${{ steps.find-tags.outputs.latest_tag }} | grep -v -i "^- \(feat\|fix\|docs\)" >> current_changelog.md || echo "- No other changes" >> current_changelog.md else echo "## Initial Release" > current_changelog.md echo "" >> current_changelog.md - echo "First release of RunAgent Universal AI Agent Platform" >> current_changelog.md + echo "πŸŽ‰ First release of RunAgent Universal AI Agent Platform!" >> current_changelog.md + echo "" >> current_changelog.md + echo "### Features" >> current_changelog.md + echo "- Universal AI agent platform supporting multiple languages" >> current_changelog.md + echo "- Python, JavaScript, Rust, and Go SDKs" >> current_changelog.md + echo "- Framework-agnostic agent deployment" >> current_changelog.md + echo "- Real-time streaming support" >> current_changelog.md fi - - CHANGELOG_CONTENT=$(cat current_changelog.md) - echo "changelog<> $GITHUB_OUTPUT - echo "$CHANGELOG_CONTENT" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT + fi + + # Read the changelog content + CHANGELOG_CONTENT=$(cat current_changelog.md) + + # Save changelog content for use in release body + echo "changelog<> $GITHUB_OUTPUT + echo "$CHANGELOG_CONTENT" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + echo "βœ… Changelog generated successfully" + echo "πŸ“„ Preview:" + echo "----------" + head -15 current_changelog.md + if [ $(wc -l < current_changelog.md) -gt 15 ]; then + echo "... ($(wc -l < current_changelog.md) total lines)" + fi + + - name: Update CHANGELOG.md in repository + if: steps.check-release.outputs.should_release == 'true' + run: | + echo "πŸ“ Updating CHANGELOG.md in repository..." + + # Create the new changelog entry + echo "# Changelog" > CHANGELOG_NEW.md + echo "" >> CHANGELOG_NEW.md + echo "## [v${{ steps.find-tags.outputs.version }}] - $(date +%Y-%m-%d)" >> CHANGELOG_NEW.md + echo "" >> CHANGELOG_NEW.md + cat current_changelog.md >> CHANGELOG_NEW.md + echo "" >> CHANGELOG_NEW.md + + # Append existing changelog if it exists + if [ -f "CHANGELOG.md" ] && [ -s "CHANGELOG.md" ]; then + # Skip the header of existing changelog + tail -n +2 CHANGELOG.md >> CHANGELOG_NEW.md + fi + + mv CHANGELOG_NEW.md CHANGELOG.md + + echo "βœ… CHANGELOG.md updated" + + - name: Commit updated CHANGELOG.md + if: steps.check-release.outputs.should_release == 'true' + run: | + echo "πŸ’Ύ Committing updated CHANGELOG.md..." + + # Configure git + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + # Add and commit the changelog + git add CHANGELOG.md + if git diff --staged --quiet; then + echo "No changes to commit" + else + git commit -m "docs: update CHANGELOG.md for v${{ steps.find-tags.outputs.version }}" + git push origin main + echo "βœ… CHANGELOG.md committed and pushed" fi - name: Create main GitHub Release + if: steps.check-release.outputs.should_release == 'true' uses: softprops/action-gh-release@v1 with: - tag_name: ${{ steps.extract_version.outputs.tag_name }} - name: RunAgent v${{ steps.extract_version.outputs.version }} + tag_name: ${{ steps.find-tags.outputs.latest_tag }} + name: RunAgent v${{ steps.find-tags.outputs.version }} body: | - # πŸš€ RunAgent v${{ steps.extract_version.outputs.version }} + # πŸš€ RunAgent v${{ steps.find-tags.outputs.version }} - **Universal AI Agent Platform - All SDKs synchronized at v${{ steps.extract_version.outputs.version }}** + **Universal AI Agent Platform - All SDKs synchronized at v${{ steps.find-tags.outputs.version }}** ## πŸ“¦ Installation @@ -95,23 +219,23 @@ jobs: ### Python ```bash - pip install runagent==${{ steps.extract_version.outputs.version }} + pip install runagent==${{ steps.find-tags.outputs.version }} ``` ### JavaScript/TypeScript ```bash - npm install runagent@${{ steps.extract_version.outputs.version }} + npm install runagent@${{ steps.find-tags.outputs.version }} ``` ### Rust ```toml [dependencies] - runagent = "${{ steps.extract_version.outputs.version }}" + runagent = "${{ steps.find-tags.outputs.version }}" ``` ### Go ```bash - go get github.com/runagent-dev/runagent/runagent-go@${{ steps.extract_version.outputs.tag_name }} + go get github.com/runagent-dev/runagent/runagent-go@${{ steps.find-tags.outputs.latest_tag }} ``` ## πŸ“‹ Changes in this Release @@ -120,10 +244,10 @@ jobs: ## πŸ”— Package Links - - 🐍 [PyPI Package](https://pypi.org/project/runagent/${{ steps.extract_version.outputs.version }}/) - - πŸ“¦ [npm Package](https://www.npmjs.com/package/runagent/v/${{ steps.extract_version.outputs.version }}) - - πŸ¦€ [crates.io Package](https://crates.io/crates/runagent/${{ steps.extract_version.outputs.version }}) - - 🐹 [Go Module](https://pkg.go.dev/github.com/runagent-dev/runagent/runagent-go@${{ steps.extract_version.outputs.tag_name }}) + - 🐍 [PyPI Package](https://pypi.org/project/runagent/${{ steps.find-tags.outputs.version }}/) (Publishing...) + - πŸ“¦ [npm Package](https://www.npmjs.com/package/runagent/v/${{ steps.find-tags.outputs.version }}) (Publishing...) + - πŸ¦€ [crates.io Package](https://crates.io/crates/runagent/${{ steps.find-tags.outputs.version }}) (Publishing...) + - 🐹 [Go Module](https://pkg.go.dev/github.com/runagent-dev/runagent/runagent-go@${{ steps.find-tags.outputs.latest_tag }}) βœ… ## πŸ“š Documentation @@ -133,11 +257,11 @@ jobs: --- - **Full Changelog**: https://github.com/runagent-dev/runagent/compare/v${{ steps.extract_version.outputs.version }} + **Note**: Individual SDK status will be updated below as packages are published. draft: false prerelease: false generate_release_notes: false files: | CHANGELOG.md env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN } \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From a20967e6879c8d40db4f77e4753340830c0f3c57 Mon Sep 17 00:00:00 2001 From: sawradip Date: Tue, 29 Jul 2025 18:34:29 +0600 Subject: [PATCH 16/16] chore: bump version to v0.1.9 --- pyproject.toml | 6 +++--- runagent-go/runagent/version.go | 2 +- runagent-rust/runagent/Cargo.toml | 2 +- runagent-ts/package-lock.json | 4 ++-- runagent-ts/package.json | 2 +- runagent/__version__.py | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a994ccd..ca4df23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "hatchling.build" [project] name = "runagent" -version = "0.1.8" +version = "0.1.9" description = "A command-line tool and SDK for deploying, managing, and interacting with AI agents" readme = "README.md" requires-python = ">=3.9" @@ -103,7 +103,7 @@ line_length = 88 skip = ["docs"] [tool.mypy] -python_version = "0.1.8" +python_version = "0.1.9" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true @@ -159,7 +159,7 @@ fail_under = 80 [tool.ruff] line-length = 88 -target-version = "0.1.8" +target-version = "0.1.9" select = [ "E", # pycodestyle errors "W", # pycodestyle warnings diff --git a/runagent-go/runagent/version.go b/runagent-go/runagent/version.go index 5180553..26f0308 100644 --- a/runagent-go/runagent/version.go +++ b/runagent-go/runagent/version.go @@ -1,4 +1,4 @@ package runagent // Version represents the current version of the RunAgent Go SDK -const Version = "0.1.8" +const Version = "0.1.9" diff --git a/runagent-rust/runagent/Cargo.toml b/runagent-rust/runagent/Cargo.toml index 40c42fd..d2b1f6f 100644 --- a/runagent-rust/runagent/Cargo.toml +++ b/runagent-rust/runagent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runagent" -version = "0.1.8" +version = "0.1.9" edition = "2021" description = "RunAgent SDK for Rust - Deploy and manage AI agents easily" license = "MIT" diff --git a/runagent-ts/package-lock.json b/runagent-ts/package-lock.json index a724aef..9e9d934 100644 --- a/runagent-ts/package-lock.json +++ b/runagent-ts/package-lock.json @@ -1,12 +1,12 @@ { "name": "runagent", - "version": "0.1.8", + "version": "0.1.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "runagent", - "version": "0.1.8", + "version": "0.1.9", "dependencies": { "better-sqlite3": "^12.2.0" }, diff --git a/runagent-ts/package.json b/runagent-ts/package.json index 40110fb..4fb641e 100644 --- a/runagent-ts/package.json +++ b/runagent-ts/package.json @@ -1,6 +1,6 @@ { "name": "runagent", - "version": "0.1.8", + "version": "0.1.9", "type": "module", "files": [ "dist" diff --git a/runagent/__version__.py b/runagent/__version__.py index 9cb17e7..c11f861 100644 --- a/runagent/__version__.py +++ b/runagent/__version__.py @@ -1 +1 @@ -__version__ = "0.1.8" +__version__ = "0.1.9"