|  | 
| 13 | 13 | import uuid | 
| 14 | 14 | from typing import TYPE_CHECKING, Any, AsyncGenerator, cast | 
| 15 | 15 | 
 | 
|  | 16 | +from opentelemetry import trace as trace_api | 
|  | 17 | + | 
| 16 | 18 | from ..experimental.hooks import ( | 
| 17 | 19 |     AfterModelInvocationEvent, | 
| 18 | 20 |     AfterToolInvocationEvent, | 
| @@ -114,72 +116,75 @@ async def event_loop_cycle(agent: "Agent", invocation_state: dict[str, Any]) -> | 
| 114 | 116 |             parent_span=cycle_span, | 
| 115 | 117 |             model_id=model_id, | 
| 116 | 118 |         ) | 
| 117 |  | - | 
| 118 |  | -        tool_specs = agent.tool_registry.get_all_tool_specs() | 
| 119 |  | - | 
| 120 |  | -        agent.hooks.invoke_callbacks( | 
| 121 |  | -            BeforeModelInvocationEvent( | 
| 122 |  | -                agent=agent, | 
| 123 |  | -            ) | 
| 124 |  | -        ) | 
| 125 |  | - | 
| 126 |  | -        try: | 
| 127 |  | -            # TODO: To maintain backwards compatibility, we need to combine the stream event with invocation_state | 
| 128 |  | -            #       before yielding to the callback handler. This will be revisited when migrating to strongly | 
| 129 |  | -            #       typed events. | 
| 130 |  | -            async for event in stream_messages(agent.model, agent.system_prompt, agent.messages, tool_specs): | 
| 131 |  | -                if "callback" in event: | 
| 132 |  | -                    yield { | 
| 133 |  | -                        "callback": {**event["callback"], **(invocation_state if "delta" in event["callback"] else {})} | 
| 134 |  | -                    } | 
| 135 |  | - | 
| 136 |  | -            stop_reason, message, usage, metrics = event["stop"] | 
| 137 |  | -            invocation_state.setdefault("request_state", {}) | 
|  | 119 | +        with trace_api.use_span(model_invoke_span): | 
|  | 120 | +            tool_specs = agent.tool_registry.get_all_tool_specs() | 
| 138 | 121 | 
 | 
| 139 | 122 |             agent.hooks.invoke_callbacks( | 
| 140 |  | -                AfterModelInvocationEvent( | 
|  | 123 | +                BeforeModelInvocationEvent( | 
| 141 | 124 |                     agent=agent, | 
| 142 |  | -                    stop_response=AfterModelInvocationEvent.ModelStopResponse( | 
| 143 |  | -                        stop_reason=stop_reason, | 
| 144 |  | -                        message=message, | 
| 145 |  | -                    ), | 
| 146 | 125 |                 ) | 
| 147 | 126 |             ) | 
| 148 | 127 | 
 | 
| 149 |  | -            if model_invoke_span: | 
| 150 |  | -                tracer.end_model_invoke_span(model_invoke_span, message, usage, stop_reason) | 
| 151 |  | -            break  # Success! Break out of retry loop | 
| 152 |  | - | 
| 153 |  | -        except Exception as e: | 
| 154 |  | -            if model_invoke_span: | 
| 155 |  | -                tracer.end_span_with_error(model_invoke_span, str(e), e) | 
| 156 |  | - | 
| 157 |  | -            agent.hooks.invoke_callbacks( | 
| 158 |  | -                AfterModelInvocationEvent( | 
| 159 |  | -                    agent=agent, | 
| 160 |  | -                    exception=e, | 
|  | 128 | +            try: | 
|  | 129 | +                # TODO: To maintain backwards compatibility, we need to combine the stream event with invocation_state | 
|  | 130 | +                #       before yielding to the callback handler. This will be revisited when migrating to strongly | 
|  | 131 | +                #       typed events. | 
|  | 132 | +                async for event in stream_messages(agent.model, agent.system_prompt, agent.messages, tool_specs): | 
|  | 133 | +                    if "callback" in event: | 
|  | 134 | +                        yield { | 
|  | 135 | +                            "callback": { | 
|  | 136 | +                                **event["callback"], | 
|  | 137 | +                                **(invocation_state if "delta" in event["callback"] else {}), | 
|  | 138 | +                            } | 
|  | 139 | +                        } | 
|  | 140 | + | 
|  | 141 | +                stop_reason, message, usage, metrics = event["stop"] | 
|  | 142 | +                invocation_state.setdefault("request_state", {}) | 
|  | 143 | + | 
|  | 144 | +                agent.hooks.invoke_callbacks( | 
|  | 145 | +                    AfterModelInvocationEvent( | 
|  | 146 | +                        agent=agent, | 
|  | 147 | +                        stop_response=AfterModelInvocationEvent.ModelStopResponse( | 
|  | 148 | +                            stop_reason=stop_reason, | 
|  | 149 | +                            message=message, | 
|  | 150 | +                        ), | 
|  | 151 | +                    ) | 
| 161 | 152 |                 ) | 
| 162 |  | -            ) | 
| 163 | 153 | 
 | 
| 164 |  | -            if isinstance(e, ModelThrottledException): | 
| 165 |  | -                if attempt + 1 == MAX_ATTEMPTS: | 
| 166 |  | -                    yield {"callback": {"force_stop": True, "force_stop_reason": str(e)}} | 
| 167 |  | -                    raise e | 
|  | 154 | +                if model_invoke_span: | 
|  | 155 | +                    tracer.end_model_invoke_span(model_invoke_span, message, usage, stop_reason) | 
|  | 156 | +                break  # Success! Break out of retry loop | 
| 168 | 157 | 
 | 
| 169 |  | -                logger.debug( | 
| 170 |  | -                    "retry_delay_seconds=<%s>, max_attempts=<%s>, current_attempt=<%s> " | 
| 171 |  | -                    "| throttling exception encountered " | 
| 172 |  | -                    "| delaying before next retry", | 
| 173 |  | -                    current_delay, | 
| 174 |  | -                    MAX_ATTEMPTS, | 
| 175 |  | -                    attempt + 1, | 
|  | 158 | +            except Exception as e: | 
|  | 159 | +                if model_invoke_span: | 
|  | 160 | +                    tracer.end_span_with_error(model_invoke_span, str(e), e) | 
|  | 161 | + | 
|  | 162 | +                agent.hooks.invoke_callbacks( | 
|  | 163 | +                    AfterModelInvocationEvent( | 
|  | 164 | +                        agent=agent, | 
|  | 165 | +                        exception=e, | 
|  | 166 | +                    ) | 
| 176 | 167 |                 ) | 
| 177 |  | -                time.sleep(current_delay) | 
| 178 |  | -                current_delay = min(current_delay * 2, MAX_DELAY) | 
| 179 | 168 | 
 | 
| 180 |  | -                yield {"callback": {"event_loop_throttled_delay": current_delay, **invocation_state}} | 
| 181 |  | -            else: | 
| 182 |  | -                raise e | 
|  | 169 | +                if isinstance(e, ModelThrottledException): | 
|  | 170 | +                    if attempt + 1 == MAX_ATTEMPTS: | 
|  | 171 | +                        yield {"callback": {"force_stop": True, "force_stop_reason": str(e)}} | 
|  | 172 | +                        raise e | 
|  | 173 | + | 
|  | 174 | +                    logger.debug( | 
|  | 175 | +                        "retry_delay_seconds=<%s>, max_attempts=<%s>, current_attempt=<%s> " | 
|  | 176 | +                        "| throttling exception encountered " | 
|  | 177 | +                        "| delaying before next retry", | 
|  | 178 | +                        current_delay, | 
|  | 179 | +                        MAX_ATTEMPTS, | 
|  | 180 | +                        attempt + 1, | 
|  | 181 | +                    ) | 
|  | 182 | +                    time.sleep(current_delay) | 
|  | 183 | +                    current_delay = min(current_delay * 2, MAX_DELAY) | 
|  | 184 | + | 
|  | 185 | +                    yield {"callback": {"event_loop_throttled_delay": current_delay, **invocation_state}} | 
|  | 186 | +                else: | 
|  | 187 | +                    raise e | 
| 183 | 188 | 
 | 
| 184 | 189 |     try: | 
| 185 | 190 |         # Add message in trace and mark the end of the stream messages trace | 
|  | 
0 commit comments