Skip to content

Commit f664214

Browse files
committed
Fix type annotations in overload_client to preserve method return types
1 parent 5cfa8de commit f664214

File tree

2 files changed

+33
-24
lines changed

2 files changed

+33
-24
lines changed

src/humanloop/client.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,34 @@
1+
import logging
12
import os
23
import typing
34
from typing import Any, List, Optional, Sequence, Tuple
4-
import logging
55

66
import httpx
77
from opentelemetry.sdk.resources import Resource
88
from opentelemetry.sdk.trace import TracerProvider
99
from opentelemetry.trace import Tracer
1010

11+
from humanloop.base_client import AsyncBaseHumanloop, BaseHumanloop
1112
from humanloop.core.client_wrapper import SyncClientWrapper
12-
13+
from humanloop.decorators.flow import flow as flow_decorator_factory
14+
from humanloop.decorators.prompt import prompt_decorator_factory
15+
from humanloop.decorators.tool import tool_decorator_factory as tool_decorator_factory
16+
from humanloop.environment import HumanloopEnvironment
1317
from humanloop.evals import run_eval
1418
from humanloop.evals.types import (
1519
DatasetEvalConfig,
16-
EvaluatorEvalConfig,
1720
EvaluatorCheck,
21+
EvaluatorEvalConfig,
1822
FileEvalConfig,
1923
)
20-
21-
from humanloop.base_client import AsyncBaseHumanloop, BaseHumanloop
22-
from humanloop.overload import overload_client
23-
from humanloop.decorators.flow import flow as flow_decorator_factory
24-
from humanloop.decorators.prompt import prompt_decorator_factory
25-
from humanloop.decorators.tool import tool_decorator_factory as tool_decorator_factory
26-
from humanloop.environment import HumanloopEnvironment
2724
from humanloop.evaluations.client import EvaluationsClient
2825
from humanloop.otel import instrument_provider
2926
from humanloop.otel.exporter import HumanloopSpanExporter
3027
from humanloop.otel.processor import HumanloopSpanProcessor
28+
from humanloop.overload import overload_client
3129
from humanloop.prompt_utils import populate_template
3230
from humanloop.prompts.client import PromptsClient
33-
from humanloop.sync.sync_client import SyncClient, DEFAULT_CACHE_SIZE
31+
from humanloop.sync.sync_client import DEFAULT_CACHE_SIZE, SyncClient
3432

3533
logger = logging.getLogger("humanloop.sdk")
3634

@@ -168,6 +166,7 @@ def __init__(
168166

169167
# Overload the .log method of the clients to be aware of Evaluation Context
170168
# and the @flow decorator providing the trace_id
169+
# Additionally, call and log methods are overloaded in the prompts and agents client to support the use of local files
171170
self.prompts = overload_client(
172171
client=self.prompts, sync_client=self._sync_client, use_local_files=self.use_local_files
173172
)

src/humanloop/overload.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
11
import inspect
22
import logging
33
import types
4-
from typing import Any, Dict, Optional, Union, Callable
4+
from typing import Any, Callable, Dict, Optional, Union, TypeVar, Protocol
55

6+
from humanloop.agents.client import AgentsClient
67
from humanloop.context import (
78
get_decorator_context,
89
get_evaluation_context,
910
get_trace_id,
1011
)
12+
from humanloop.datasets.client import DatasetsClient
1113
from humanloop.error import HumanloopRuntimeError
12-
from humanloop.sync.sync_client import SyncClient
13-
from humanloop.prompts.client import PromptsClient
14+
from humanloop.evaluators.client import EvaluatorsClient
1415
from humanloop.flows.client import FlowsClient
15-
from humanloop.datasets.client import DatasetsClient
16-
from humanloop.agents.client import AgentsClient
16+
from humanloop.prompts.client import PromptsClient
17+
from humanloop.sync.sync_client import SyncClient
1718
from humanloop.tools.client import ToolsClient
18-
from humanloop.evaluators.client import EvaluatorsClient
1919
from humanloop.types import FileType
20+
from humanloop.types.agent_call_response import AgentCallResponse
2021
from humanloop.types.create_evaluator_log_response import CreateEvaluatorLogResponse
2122
from humanloop.types.create_flow_log_response import CreateFlowLogResponse
2223
from humanloop.types.create_prompt_log_response import CreatePromptLogResponse
2324
from humanloop.types.create_tool_log_response import CreateToolLogResponse
2425
from humanloop.types.prompt_call_response import PromptCallResponse
25-
from humanloop.types.agent_call_response import AgentCallResponse
2626

2727
logger = logging.getLogger("humanloop.sdk")
2828

29+
2930
LogResponseType = Union[
3031
CreatePromptLogResponse,
3132
CreateToolLogResponse,
@@ -39,6 +40,9 @@
3940
]
4041

4142

43+
T = TypeVar("T", bound=Union[PromptsClient, AgentsClient, ToolsClient, FlowsClient, DatasetsClient, EvaluatorsClient])
44+
45+
4246
def _get_file_type_from_client(
4347
client: Union[PromptsClient, AgentsClient, ToolsClient, FlowsClient, DatasetsClient, EvaluatorsClient],
4448
) -> FileType:
@@ -184,33 +188,39 @@ def _overload_call(self: Any, sync_client: Optional[SyncClient], use_local_files
184188

185189

186190
def overload_client(
187-
client: Any,
191+
client: T,
188192
sync_client: Optional[SyncClient] = None,
189193
use_local_files: bool = False,
190-
) -> Any:
194+
) -> T:
191195
"""Overloads client methods to add tracing, local file handling, and evaluation context."""
192196
# Store original log method as _log for all clients. Used in flow decorator
193197
if hasattr(client, "log") and not hasattr(client, "_log"):
194-
client._log = client.log # type: ignore[attr-defined]
198+
# Store original method - using getattr/setattr to avoid type errors
199+
original_log = getattr(client, "log")
200+
setattr(client, "_log", original_log)
195201

196202
# Create a closure to capture sync_client and use_local_files
197203
def log_wrapper(self: Any, **kwargs) -> LogResponseType:
198204
return _overload_log(self, sync_client, use_local_files, **kwargs)
199205

200-
client.log = types.MethodType(log_wrapper, client)
206+
# Replace the log method
207+
setattr(client, "log", types.MethodType(log_wrapper, client))
201208

202209
# Overload call method for Prompt and Agent clients
203210
if _get_file_type_from_client(client) in ["prompt", "agent"]:
204211
if sync_client is None and use_local_files:
205212
logger.error("sync_client is None but client has call method and use_local_files=%s", use_local_files)
206213
raise HumanloopRuntimeError("sync_client is required for clients that support call operations")
207214
if hasattr(client, "call") and not hasattr(client, "_call"):
208-
client._call = client.call # type: ignore[attr-defined]
215+
# Store original method - using getattr/setattr to avoid type errors
216+
original_call = getattr(client, "call")
217+
setattr(client, "_call", original_call)
209218

210219
# Create a closure to capture sync_client and use_local_files
211220
def call_wrapper(self: Any, **kwargs) -> CallResponseType:
212221
return _overload_call(self, sync_client, use_local_files, **kwargs)
213222

214-
client.call = types.MethodType(call_wrapper, client)
223+
# Replace the call method
224+
setattr(client, "call", types.MethodType(call_wrapper, client))
215225

216226
return client

0 commit comments

Comments
 (0)