Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "utcp"
version = "0.2.0"
version = "0.2.1"
authors = [
{ name = "Razvan-Ion Radulescu" },
{ name = "Andrei-Stefan Ghiurtu" },
Expand Down
67 changes: 67 additions & 0 deletions src/utcp/client/client_transport_interface.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,84 @@
"""Abstract interface for UTCP client transport implementations.

This module defines the contract that all transport implementations must follow
to integrate with the UTCP client. Transport implementations handle the actual
communication with different types of tool providers (HTTP, CLI, WebSocket, etc.).
"""

from abc import ABC, abstractmethod
from typing import Dict, Any, List
from utcp.shared.provider import Provider
from utcp.shared.tool import Tool

class ClientTransportInterface(ABC):
"""Abstract interface for UTCP client transport implementations.

Defines the contract that all transport implementations must follow to
integrate with the UTCP client. Each transport handles communication
with a specific type of provider (HTTP, CLI, WebSocket, etc.).

Transport implementations are responsible for:
- Discovering available tools from providers
- Managing provider lifecycle (registration/deregistration)
- Executing tool calls through the appropriate protocol
"""

@abstractmethod
async def register_tool_provider(self, manual_provider: Provider) -> List[Tool]:
"""Register a tool provider and discover its available tools.

Connects to the provider and retrieves the list of tools it offers.
This may involve making discovery requests, parsing configuration files,
or initializing connections depending on the provider type.

Args:
manual_provider: The provider configuration to register.

Returns:
List of Tool objects discovered from the provider.

Raises:
ConnectionError: If unable to connect to the provider.
ValueError: If the provider configuration is invalid.
"""
pass

@abstractmethod
async def deregister_tool_provider(self, manual_provider: Provider) -> None:
"""Deregister a tool provider and clean up resources.

Cleanly disconnects from the provider and releases any associated
resources such as connections, processes, or file handles.

Args:
manual_provider: The provider configuration to deregister.

Note:
Should handle cases where the provider is already disconnected
or was never properly registered.
"""
pass

@abstractmethod
async def call_tool(self, tool_name: str, arguments: Dict[str, Any], tool_provider: Provider) -> Any:
"""Execute a tool call through this transport.

Sends a tool invocation request to the provider using the appropriate
protocol and returns the result. Handles serialization of arguments
and deserialization of responses according to the transport type.

Args:
tool_name: Name of the tool to call (may include provider prefix).
arguments: Dictionary of arguments to pass to the tool.
tool_provider: Provider configuration for the tool.

Returns:
The tool's response, with type depending on the tool's output schema.

Raises:
ToolNotFoundError: If the specified tool doesn't exist.
ValidationError: If the arguments don't match the tool's input schema.
ConnectionError: If unable to communicate with the provider.
TimeoutError: If the tool call exceeds the configured timeout.
"""
pass
57 changes: 56 additions & 1 deletion src/utcp/client/openapi_converter.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
"""OpenAPI specification converter for UTCP tool generation.

This module provides functionality to convert OpenAPI specifications (both 2.0
and 3.0) into UTCP tool definitions. It handles schema resolution, authentication
mapping, and proper tool creation from REST API specifications.

Key Features:
- OpenAPI 2.0 and 3.0 specification support
- Automatic JSON reference ($ref) resolution
- Authentication scheme mapping (API key, Basic, OAuth2)
- Input/output schema extraction from OpenAPI schemas
- URL path parameter handling
- Request body and header field mapping
- Provider name generation from specification metadata

The converter creates UTCP tools that can be used to interact with REST APIs
defined by OpenAPI specifications, providing a bridge between OpenAPI and UTCP.
"""

import json
from typing import Any, Dict, List, Optional, Tuple
import sys
Expand All @@ -11,9 +30,45 @@


class OpenApiConverter:
"""Converts an OpenAPI JSON specification into a UtcpManual."""
"""Converts OpenAPI specifications into UTCP tool definitions.

Processes OpenAPI 2.0 and 3.0 specifications to generate equivalent UTCP
tools, handling schema resolution, authentication mapping, and proper
HTTP provider configuration. Each operation in the OpenAPI spec becomes
a UTCP tool with appropriate input/output schemas.

Features:
- Complete OpenAPI specification parsing
- Recursive JSON reference ($ref) resolution
- Authentication scheme conversion (API key, Basic, OAuth2)
- Input parameter and request body handling
- Response schema extraction
- URL template and path parameter support
- Provider name normalization
- Placeholder variable generation for configuration

Architecture:
The converter works by iterating through all paths and operations
in the OpenAPI spec, extracting relevant information for each
operation, and creating corresponding UTCP tools with HTTP providers.

Attributes:
spec: The parsed OpenAPI specification dictionary.
spec_url: Optional URL where the specification was retrieved from.
placeholder_counter: Counter for generating unique placeholder variables.
provider_name: Normalized name for the provider derived from the spec.
"""

def __init__(self, openapi_spec: Dict[str, Any], spec_url: Optional[str] = None, provider_name: Optional[str] = None):
"""Initialize the OpenAPI converter.

Args:
openapi_spec: Parsed OpenAPI specification as a dictionary.
spec_url: Optional URL where the specification was retrieved from.
Used for base URL determination if servers are not specified.
provider_name: Optional custom name for the provider. If not
provided, derives name from the specification title.
"""
self.spec = openapi_spec
self.spec_url = spec_url
# Single counter for all placeholder variables
Expand Down
25 changes: 25 additions & 0 deletions src/utcp/client/tool_repository.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
"""Abstract interface for tool and provider storage.

This module defines the contract for implementing tool repositories that store
and manage UTCP tools and their associated providers. Different implementations
can provide various storage backends such as in-memory, database, or file-based
storage.
"""

from abc import ABC, abstractmethod
from typing import List, Dict, Any, Optional
from utcp.shared.provider import Provider
from utcp.shared.tool import Tool

class ToolRepository(ABC):
"""Abstract interface for tool and provider storage implementations.

Defines the contract for repositories that manage the lifecycle and storage
of UTCP tools and providers. Repositories are responsible for:
- Persisting provider configurations and their associated tools
- Providing efficient lookup and retrieval operations
- Managing relationships between providers and tools
- Ensuring data consistency during operations

The repository interface supports both individual and bulk operations,
allowing for flexible implementation strategies ranging from simple
in-memory storage to sophisticated database backends.

Note:
All methods are async to support both synchronous and asynchronous
storage implementations.
"""
@abstractmethod
async def save_provider_with_tools(self, provider: Provider, tools: List[Tool]) -> None:
"""
Expand Down
73 changes: 64 additions & 9 deletions src/utcp/client/tool_search_strategies/tag_search.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
"""Tag-based tool search strategy implementation.

This module provides a search strategy that ranks tools based on tag matches
and description keyword matches. It implements a weighted scoring system where
explicit tag matches receive higher scores than description word matches.
"""

from utcp.client.tool_search_strategy import ToolSearchStrategy
from typing import List, Dict, Tuple
from utcp.shared.tool import Tool
Expand All @@ -6,25 +13,73 @@
import asyncio

class TagSearchStrategy(ToolSearchStrategy):
"""Tag-based search strategy for UTCP tools.

Implements a weighted scoring algorithm that matches search queries against
tool tags and descriptions. Explicit tag matches receive full weight while
description word matches receive reduced weight.

Scoring Algorithm:
- Exact tag matches: Weight 1.0
- Tag word matches: Weight equal to description_weight
- Description word matches: Weight equal to description_weight
- Only considers description words longer than 2 characters

Examples:
>>> strategy = TagSearchStrategy(repository, description_weight=0.3)
>>> tools = await strategy.search_tools("weather api", limit=5)
>>> # Returns tools with "weather" or "api" tags/descriptions

Attributes:
tool_repository: Repository to search for tools.
description_weight: Weight multiplier for description matches (0.0-1.0).
"""

def __init__(self, tool_repository: ToolRepository, description_weight: float = 0.3):
"""Initialize the tag search strategy.

Args:
tool_repository: Repository containing tools to search.
description_weight: Weight for description word matches relative to
tag matches. Should be between 0.0 and 1.0, where 1.0 gives
equal weight to tags and descriptions.

Raises:
ValueError: If description_weight is not between 0.0 and 1.0.
"""
if not 0.0 <= description_weight <= 1.0:
raise ValueError("description_weight must be between 0.0 and 1.0")

self.tool_repository = tool_repository
# Weight for description words vs explicit tags (explicit tags have weight of 1.0)
self.description_weight = description_weight

async def search_tools(self, query: str, limit: int = 10) -> List[Tool]:
"""
Return tools ordered by tag occurrences in the query.

Uses both explicit tags and words from tool descriptions (with less weight).

"""Search tools using tag and description matching.

Implements a weighted scoring system that ranks tools based on how well
their tags and descriptions match the search query. Normalizes the query
and uses word-based matching with configurable weights.

Scoring Details:
- Exact tag matches in query: +1.0 points
- Individual tag words matching query words: +description_weight points
- Description words matching query words: +description_weight points
- Only description words > 2 characters are considered

Args:
query: The search query string
limit: Maximum number of tools to return
query: Search query string. Case-insensitive, word-based matching.
limit: Maximum number of tools to return. Must be >= 0.

Returns:
List of tools ordered by relevance to the query
List of Tool objects ranked by relevance score (highest first).
Empty list if no tools match or repository is empty.

Raises:
ValueError: If limit is negative.
"""
if limit < 0:
raise ValueError("limit must be non-negative")
# Normalize query to lowercase and split into words
query_lower = query.lower()
# Extract words from the query, filtering out non-word characters
Expand Down
40 changes: 35 additions & 5 deletions src/utcp/client/tool_search_strategy.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,48 @@
"""Abstract interface for tool search strategies.

This module defines the contract for implementing tool search and ranking
algorithms. Different strategies can implement various approaches such as
tag-based search, semantic search, or hybrid approaches.
"""

from abc import ABC, abstractmethod
from typing import List
from utcp.shared.tool import Tool

class ToolSearchStrategy(ABC):
"""Abstract interface for tool search implementations.

Defines the contract for tool search strategies that can be plugged into
the UTCP client. Different implementations can provide various search
algorithms such as tag-based matching, semantic similarity, or keyword
search.

Search strategies are responsible for:
- Interpreting search queries
- Ranking tools by relevance
- Limiting results appropriately
- Providing consistent search behavior
"""

@abstractmethod
async def search_tools(self, query: str, limit: int = 10) -> List[Tool]:
"""
Search for tools relevant to the query.
"""Search for tools relevant to the query.

Executes a search against the available tools and returns the most
relevant matches ranked by the strategy's scoring algorithm.

Args:
query: The search query.
limit: The maximum number of tools to return. 0 for no limit.
query: The search query string. Format depends on the strategy
(e.g., keywords, tags, natural language).
limit: Maximum number of tools to return. Use 0 for no limit.
Strategies should respect this limit for performance.

Returns:
A list of tools that match the search query.
List of Tool objects ranked by relevance, limited to the
specified count. Empty list if no matches found.

Raises:
ValueError: If the query format is invalid for this strategy.
RuntimeError: If the search operation fails unexpectedly.
"""
pass
Loading
Loading