11from utcp .data .call_template import CallTemplate
2- from utcp .data .auth import Auth
2+ from utcp .data .auth import Auth , AuthSerializer
3+ from utcp .interfaces .serializer import Serializer
4+ from utcp .exceptions import UtcpSerializerValidationError
5+ import traceback
36from typing import Dict , List , Optional , Literal
4- from pydantic import Field
7+ from pydantic import Field , field_serializer , field_validator
58
6- class GraphQLProvider (CallTemplate ):
9+ class GraphQLCallTemplate (CallTemplate ):
710 """Provider configuration for GraphQL-based tools.
811
912 Enables communication with GraphQL endpoints supporting queries, mutations,
1013 and subscriptions. Provides flexible query execution with custom headers
1114 and authentication.
1215
16+ For maximum flexibility, use the `query` field to provide a complete GraphQL
17+ query string with proper selection sets and variable types. This allows agents
18+ to call any existing GraphQL endpoint without limitations.
19+
1320 Attributes:
1421 call_template_type: Always "graphql" for GraphQL providers.
1522 url: The GraphQL endpoint URL.
@@ -18,6 +25,23 @@ class GraphQLProvider(CallTemplate):
1825 auth: Optional authentication configuration.
1926 headers: Optional static headers to include in requests.
2027 header_fields: List of tool argument names to map to HTTP request headers.
28+ query: Custom GraphQL query string with full control over selection sets
29+ and variable types. Example: 'query GetUser($id: ID!) { user(id: $id) { id name } }'
30+ variable_types: Map of variable names to GraphQL types for auto-generated queries.
31+ Example: {'id': 'ID!', 'limit': 'Int'}. Defaults to 'String' if not specified.
32+
33+ Example:
34+ # Full flexibility with custom query
35+ template = GraphQLCallTemplate(
36+ url="https://api.example.com/graphql",
37+ query="query GetUser($id: ID!) { user(id: $id) { id name email } }",
38+ )
39+
40+ # Auto-generation with proper types
41+ template = GraphQLCallTemplate(
42+ url="https://api.example.com/graphql",
43+ variable_types={"limit": "Int", "active": "Boolean"},
44+ )
2145 """
2246
2347 call_template_type : Literal ["graphql" ] = "graphql"
@@ -27,3 +51,43 @@ class GraphQLProvider(CallTemplate):
2751 auth : Optional [Auth ] = None
2852 headers : Optional [Dict [str , str ]] = None
2953 header_fields : Optional [List [str ]] = Field (default = None , description = "List of input fields to be sent as request headers for the initial connection." )
54+ query : Optional [str ] = Field (
55+ default = None ,
56+ description = "Custom GraphQL query/mutation string. Use $varName syntax for variables. "
57+ "If provided, this takes precedence over auto-generation. "
58+ "Example: 'query GetUser($id: ID!) { user(id: $id) { id name email } }'"
59+ )
60+ variable_types : Optional [Dict [str , str ]] = Field (
61+ default = None ,
62+ description = "Map of variable names to GraphQL types for auto-generated queries. "
63+ "Example: {'id': 'ID!', 'limit': 'Int', 'active': 'Boolean'}. "
64+ "Defaults to 'String' if not specified."
65+ )
66+
67+ @field_serializer ("auth" )
68+ def serialize_auth (self , auth : Optional [Auth ]):
69+ if auth is None :
70+ return None
71+ return AuthSerializer ().to_dict (auth )
72+
73+ @field_validator ("auth" , mode = "before" )
74+ @classmethod
75+ def validate_auth (cls , v : Optional [Auth | dict ]):
76+ if v is None :
77+ return None
78+ if isinstance (v , Auth ):
79+ return v
80+ return AuthSerializer ().validate_dict (v )
81+
82+
83+ class GraphQLCallTemplateSerializer (Serializer [GraphQLCallTemplate ]):
84+ def to_dict (self , obj : GraphQLCallTemplate ) -> dict :
85+ return obj .model_dump ()
86+
87+ def validate_dict (self , data : dict ) -> GraphQLCallTemplate :
88+ try :
89+ return GraphQLCallTemplate .model_validate (data )
90+ except Exception as e :
91+ raise UtcpSerializerValidationError (
92+ f"Invalid GraphQLCallTemplate: { e } \n { traceback .format_exc ()} "
93+ )
0 commit comments