diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e753460..499b7b4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,6 +17,8 @@ on: required: false type: string push: + branches: + - main tags: - 'v*' @@ -85,12 +87,6 @@ jobs: sed -i "s/version = \".*\"/version = \"$NEW_VERSION\"/" pyproject.toml echo "Updated pyproject.toml with version $NEW_VERSION" - - name: Run pre-commit checks - run: | - echo "Running code quality checks..." - uv run ruff check . - uv run ruff format --check . - - name: Build package run: | echo "Building package..." diff --git a/pyproject.toml b/pyproject.toml index 741f566..63a963a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ where = ["src"] expert = ["prompts/*.md"] [tool.ruff] -line-length = 88 +line-length = 100 target-version = "py313" [tool.ruff.lint] diff --git a/src/expert/agent.py b/src/expert/agent.py index 7163471..e379c35 100644 --- a/src/expert/agent.py +++ b/src/expert/agent.py @@ -21,6 +21,7 @@ def __init__(self, config: Config): self.agent = None self.checkpointer = InMemorySaver() self._load_prompts() + self.opik_config = None def _load_prompts(self): """Load prompt templates from files.""" @@ -91,19 +92,28 @@ async def setup(self): prompt=self.system_prompt, ) - # Initialize Opik tracer if configured + # Store Opik configuration if configured if self.config.opik_api_key: - opik_config = self.config.opik_config - project_name = opik_config.get("project_name", "invopop-expert") - - self.tracer = OpikTracer( - graph=self.agent.get_graph(xray=True), project_name=project_name - ) + self.opik_config = self.config.opik_config + project_name = self.opik_config.get("project_name", "invopop-expert") print(f"✅ Opik tracing enabled for project: {project_name}") else: - self.tracer = None + self.opik_config = None print("⚠️ Opik tracing disabled - missing API key") + def _create_opik_tracer(self, user_thread_id: str) -> OpikTracer | None: + """Create a new OpikTracer instance for the given thread_id.""" + if not self.opik_config: + return None + + project_name = self.opik_config.get("project_name", "invopop-expert") + + # Create tracer with user thread_id in metadata for proper thread tracking + tracer = OpikTracer( + graph=self.agent.get_graph(xray=True), project_name=project_name, tags=[user_thread_id] + ) + return tracer + async def get_response(self, user_input: str, thread_id: str) -> str: """Get response from the agent for a given input.""" if not self.agent: @@ -113,9 +123,11 @@ async def get_response(self, user_input: str, thread_id: str) -> str: "configurable": {"thread_id": thread_id}, } - # Add tracer callback if available - if self.tracer: - thread_config["callbacks"] = [self.tracer] + # Create a new tracer instance for this conversation + # The tracer uses the original thread_id in metadata for proper Opik thread tracking + tracer = self._create_opik_tracer(thread_id) + if tracer: + thread_config["callbacks"] = [tracer] async for chunk in self.agent.astream( {"messages": [{"role": "user", "content": user_input}]}, thread_config diff --git a/src/expert/config.py b/src/expert/config.py index ea66aee..6c0ee4d 100644 --- a/src/expert/config.py +++ b/src/expert/config.py @@ -32,9 +32,7 @@ def _load_config(self) -> dict[str, Any]: with open(self.config_path) as f: return yaml.safe_load(f) except FileNotFoundError as e: - raise FileNotFoundError( - f"Configuration file not found: {self.config_path}" - ) from e + raise FileNotFoundError(f"Configuration file not found: {self.config_path}") from e except yaml.YAMLError as e: raise ValueError(f"Invalid YAML configuration: {e}") from e @@ -92,9 +90,7 @@ def mcp_config(self) -> dict[str, Any]: # Expand home directory paths for server_config in config.values(): if "args" in server_config: - server_config["args"] = [ - os.path.expanduser(arg) for arg in server_config["args"] - ] + server_config["args"] = [os.path.expanduser(arg) for arg in server_config["args"]] return config