11from contextlib import contextmanager , redirect_stdout
2- from typing import ContextManager
2+ from dataclasses import dataclass
3+ import os
4+ from typing import Any , ContextManager , Generator
35import io
46from typing import TextIO
7+ import uuid
58import pytest
9+ import dotenv
10+ from humanloop .client import Humanloop
11+
12+
13+ @dataclass
14+ class TestIdentifiers :
15+ file_id : str
16+ file_path : str
617
718
819@pytest .fixture ()
@@ -14,3 +25,128 @@ def _context_manager():
1425 yield f
1526
1627 return _context_manager # type: ignore [return-value]
28+
29+
30+ @pytest .fixture (scope = "session" )
31+ def openai_key () -> str :
32+ dotenv .load_dotenv ()
33+ if not os .getenv ("OPENAI_API_KEY" ):
34+ pytest .fail ("OPENAI_API_KEY is not set for integration tests" )
35+ return os .getenv ("OPENAI_API_KEY" ) # type: ignore [return-value]
36+
37+
38+ @pytest .fixture (scope = "session" )
39+ def humanloop_test_client () -> Humanloop :
40+ dotenv .load_dotenv ()
41+ if not os .getenv ("HUMANLOOP_API_KEY" ):
42+ pytest .fail ("HUMANLOOP_API_KEY is not set for integration tests" )
43+ return Humanloop (api_key = os .getenv ("HUMANLOOP_API_KEY" )) # type: ignore [return-value]
44+
45+
46+ @pytest .fixture (scope = "function" )
47+ def sdk_test_dir (humanloop_test_client : Humanloop ) -> Generator [str , None , None ]:
48+ path = f"SDK_INTEGRATION_TEST_{ uuid .uuid4 ()} "
49+ try :
50+ response = humanloop_test_client .directories .create (path = path )
51+ yield response .path
52+ humanloop_test_client .directories .delete (id = response .id )
53+ except Exception as e :
54+ pytest .fail (f"Failed to create directory { path } : { e } " )
55+
56+
57+ @pytest .fixture (scope = "function" )
58+ def test_prompt_config () -> dict [str , Any ]:
59+ return {
60+ "provider" : "openai" ,
61+ "model" : "gpt-4o-mini" ,
62+ "temperature" : 0.5 ,
63+ "template" : [
64+ {
65+ "role" : "system" ,
66+ "content" : "You are a helpful assistant. You must answer the user's question truthfully and at the level of a 5th grader." ,
67+ },
68+ {
69+ "role" : "user" ,
70+ "content" : "{{question}}" ,
71+ },
72+ ],
73+ }
74+
75+
76+ @pytest .fixture (scope = "function" )
77+ def eval_dataset (humanloop_test_client : Humanloop , sdk_test_dir : str ) -> Generator [TestIdentifiers , None , None ]:
78+ dataset_path = f"{ sdk_test_dir } /eval_dataset"
79+ try :
80+ response = humanloop_test_client .datasets .upsert (
81+ path = dataset_path ,
82+ datapoints = [
83+ {
84+ "inputs" : {
85+ "question" : "What is the capital of the France?" ,
86+ },
87+ },
88+ {
89+ "inputs" : {
90+ "question" : "What is the capital of the Germany?" ,
91+ },
92+ },
93+ {
94+ "inputs" : {
95+ "question" : "What is 2+2?" ,
96+ },
97+ },
98+ ],
99+ )
100+ yield TestIdentifiers (file_id = response .id , file_path = response .path )
101+ humanloop_test_client .datasets .delete (id = response .id )
102+ except Exception as e :
103+ pytest .fail (f"Failed to create dataset { dataset_path } : { e } " )
104+
105+
106+ @pytest .fixture (scope = "function" )
107+ def eval_prompt (
108+ humanloop_test_client : Humanloop , sdk_test_dir : str , openai_key : str , test_prompt_config : dict [str , Any ]
109+ ) -> Generator [TestIdentifiers , None , None ]:
110+ prompt_path = f"{ sdk_test_dir } /eval_prompt"
111+ try :
112+ response = humanloop_test_client .prompts .upsert (
113+ path = prompt_path ,
114+ ** test_prompt_config ,
115+ )
116+ yield TestIdentifiers (file_id = response .id , file_path = response .path )
117+ humanloop_test_client .prompts .delete (id = response .id )
118+ except Exception as e :
119+ pytest .fail (f"Failed to create prompt { prompt_path } : { e } " )
120+
121+
122+ @pytest .fixture (scope = "function" )
123+ def output_not_null_evaluator (
124+ humanloop_test_client : Humanloop , sdk_test_dir : str
125+ ) -> Generator [TestIdentifiers , None , None ]:
126+ evaluator_path = f"{ sdk_test_dir } /output_not_null_evaluator"
127+ try :
128+ response = humanloop_test_client .evaluators .upsert (
129+ path = evaluator_path ,
130+ spec = {
131+ "arguments_type" : "target_required" ,
132+ "return_type" : "boolean" ,
133+ "code" : """
134+ def output_not_null(log: dict) -> bool:
135+ return log["output"] is not None
136+ """ ,
137+ "evaluator_type" : "python" ,
138+ },
139+ )
140+ yield TestIdentifiers (file_id = response .id , file_path = response .path )
141+ humanloop_test_client .evaluators .delete (id = response .id )
142+ except Exception as e :
143+ pytest .fail (f"Failed to create evaluator { evaluator_path } : { e } " )
144+
145+
146+ @pytest .fixture (scope = "function" )
147+ def id_for_staging_environment (humanloop_test_client : Humanloop , eval_prompt : TestIdentifiers ) -> str :
148+ response = humanloop_test_client .prompts .list_environments (id = eval_prompt .file_id )
149+ for environment in response :
150+ if environment .name == "staging" :
151+ return environment .id
152+ pytest .fail ("Staging environment not found" )
0 commit comments