@@ -21,80 +21,106 @@ async def run_example(
2121 options : AthenaOptions ,
2222 auth_token : str ,
2323 max_test_images : int | None = None ,
24- ) -> None :
25- """Run an example classification with the given options.
24+ ) -> tuple [ int , int ] :
25+ """Run example classification with the given options.
2626
2727 Args:
2828 logger: Logger instance for output
2929 options: Configuration options for the Athena client
3030 auth_token: Authentication token for the API
3131 max_test_images: Maximum number of test images to generate
32- (None for infinite)
32+
33+ Returns:
34+ Number of requests sent and responses received
3335
3436 """
3537 channel = create_channel (options .host , auth_token )
3638
39+ sent_counter = [0 ] # Use list to allow mutation in closure
40+ received_count = 0
41+
3742 async with AthenaClient (channel , options ) as client :
38- # Start image classification
39- results = client . classify_images ( iter_images (max_test_images ) )
40- logger . info ( "Starting classification..." )
43+ results = client . classify_images (
44+ iter_images (max_test_images , sent_counter )
45+ )
4146
42- # Initialize rate tracking
4347 start_time = time .time ()
44- response_count = 0
4548
4649 try :
4750 async for result in results :
48- response_count += 1
49- current_time = time .time ()
50- elapsed = current_time - start_time
51+ received_count += len (result .outputs )
5152
52- if response_count % 10 == 0 :
53- rate = response_count / elapsed
53+ if received_count % 10 == 0 :
54+ elapsed = time .time () - start_time
55+ rate = received_count / elapsed if elapsed > 0 else 0
5456 logger .info (
55- "Processed %d responses (%.1f/sec)" ,
56- response_count ,
57+ "Sent %d requests, received %d responses (%.1f/sec)" ,
58+ sent_counter [0 ],
59+ received_count ,
5760 rate ,
5861 )
5962
60- logger .debug ("Got result: %s" , result )
63+ for output in result .outputs :
64+ classifications = {
65+ c .label : round (c .weight , 3 )
66+ for c in output .classifications
67+ }
68+ logger .debug (
69+ "Result [%s]: %s" ,
70+ output .correlation_id [:8 ],
71+ classifications ,
72+ )
73+
6174 except Exception :
6275 logger .exception ("Error during classification" )
63- if response_count == 0 :
64- # Re-raise if we didn't process any responses
76+ if received_count == 0 :
6577 raise
6678 finally :
67- # Calculate final statistics after loop completes
68- if response_count > 0 :
69- duration = time .time () - start_time
70- avg_rate = response_count / duration
79+ duration = time .time () - start_time
80+ if received_count > 0 :
81+ avg_rate = received_count / duration if duration > 0 else 0
7182 logger .info (
72- "\n Final: %d responses in %.1f seconds (%.1f/sec)" ,
73- response_count ,
83+ "Completed: sent=%d received=%d in %.1fs (%.1f/sec)" ,
84+ sent_counter [0 ],
85+ received_count ,
7486 duration ,
7587 avg_rate ,
7688 )
7789
90+ if options .timeout and duration >= options .timeout * 0.95 :
91+ logger .info (
92+ "Stream reached maximum duration: %.1fs (limit: %.1fs)" ,
93+ duration ,
94+ options .timeout ,
95+ )
96+ elif options .timeout :
97+ logger .info (
98+ "Stream completed naturally in %.1fs (max: %.1fs)" ,
99+ duration ,
100+ options .timeout ,
101+ )
102+
103+ return (sent_counter [0 ], received_count )
104+
78105
79106async def main () -> int :
80- """Run examples showing both authenticated and unauthenticated usage ."""
107+ """Run the classification example ."""
81108 logger = logging .getLogger (__name__ )
82109 load_dotenv ()
83110
84- # Set maximum number of test images to generate (None for infinite)
85- max_test_images = None
111+ # Configuration
112+ max_test_images = 100
86113
87- # Example with authenticated channel
88114 auth_token = os .getenv ("ATHENA_AUTH_TOKEN" )
89115 if not auth_token :
90- logger .error ("ATHENA_AUTH_TOKEN not set. " )
116+ logger .error ("ATHENA_AUTH_TOKEN not set" )
91117 return 1
92118
93- logger .info ("Running example with secure authenticated channel" )
94119 host = os .getenv ("ATHENA_HOST" , "localhost" )
95- channel = create_channel ( host , auth_token )
120+ logger . info ( "Connecting to %s" , host )
96121
97- # Get the first available deployment using context manager
122+ # Get available deployment
123+ channel = create_channel (host , auth_token )
98124 async with DeploymentSelector (channel ) as deployment_selector :
99125 deployments = await deployment_selector .list_deployments ()
100126
@@ -105,15 +131,25 @@ async def main() -> int:
105131 deployment_id = deployments .deployments [0 ].deployment_id
106132 logger .info ("Using deployment: %s" , deployment_id )
107133
108- athena_options = AthenaOptions (
134+ # Run classification with persistent connection
135+ options = AthenaOptions (
109136 host = host ,
110137 resize_images = True ,
111138 deployment_id = deployment_id ,
112139 compress_images = True ,
140+ timeout = 120.0 , # Maximum duration, not forced timeout
141+ keepalive_interval = 30.0 , # Longer intervals for persistent streams
142+ )
143+
144+ sent , received = await run_example (
145+ logger , options , auth_token , max_test_images
113146 )
114- await run_example (logger , athena_options , auth_token , max_test_images )
115147
116- return 0
148+ if sent == received :
149+ logger .info ("Success: %d requests processed" , sent )
150+ return 0
151+ logger .warning ("Incomplete: %d sent, %d received" , sent , received )
152+ return 1
117153
118154
119155if __name__ == "__main__" :
0 commit comments