Synchronous version of `step_async()`. See `step_async()` for details. TODO: Except for the self.response() calls, this fn should be identical to `step_async()`. Consider refactoring to avoid duplication.
(self, turns: int = -1)
| 1204 | ) |
| 1205 | |
| 1206 | def step(self, turns: int = -1) -> ChatDocument | None: |
| 1207 | """ |
| 1208 | Synchronous version of `step_async()`. See `step_async()` for details. |
| 1209 | TODO: Except for the self.response() calls, this fn should be identical to |
| 1210 | `step_async()`. Consider refactoring to avoid duplication. |
| 1211 | """ |
| 1212 | self.is_done = False |
| 1213 | parent = self.pending_message |
| 1214 | recipient = ( |
| 1215 | "" |
| 1216 | if self.pending_message is None |
| 1217 | else self.pending_message.metadata.recipient |
| 1218 | ) |
| 1219 | if not self._valid_recipient(recipient): |
| 1220 | logger.warning(f"Invalid recipient: {recipient}") |
| 1221 | error_doc = ChatDocument( |
| 1222 | content=f"Invalid recipient: {recipient}", |
| 1223 | metadata=ChatDocMetaData( |
| 1224 | sender=Entity.AGENT, |
| 1225 | sender_name=Entity.AGENT, |
| 1226 | ), |
| 1227 | ) |
| 1228 | self._process_valid_responder_result(Entity.AGENT, parent, error_doc) |
| 1229 | return error_doc |
| 1230 | |
| 1231 | responders: List[Responder] = self.non_human_responders.copy() |
| 1232 | |
| 1233 | if ( |
| 1234 | Entity.USER in self.responders |
| 1235 | and not self.human_tried |
| 1236 | and not self.agent.has_tool_message_attempt(self.pending_message) |
| 1237 | ): |
| 1238 | # Give human first chance if they haven't been tried in last step, |
| 1239 | # and the msg is not a tool-call attempt; |
| 1240 | # (When `interactive=False`, human is only allowed to respond only if |
| 1241 | # if explicitly addressed) |
| 1242 | # This ensures human gets a chance to respond, |
| 1243 | # other than to a LLM tool-call. |
| 1244 | # When there's a tool msg attempt we want the |
| 1245 | # Agent to be the next responder; this only makes a difference in an |
| 1246 | # interactive setting: LLM generates tool, then we don't want user to |
| 1247 | # have to respond, and instead let the agent_response handle the tool. |
| 1248 | |
| 1249 | responders.insert(0, Entity.USER) |
| 1250 | |
| 1251 | found_response = False |
| 1252 | # (responder, result) from a responder who explicitly said NO_ANSWER |
| 1253 | no_answer_response: None | Tuple[Responder, ChatDocument] = None |
| 1254 | n_non_responders = 0 |
| 1255 | for r in responders: |
| 1256 | self.is_pass_thru = False |
| 1257 | if not self._can_respond(r): |
| 1258 | n_non_responders += 1 |
| 1259 | # create dummy msg for logging |
| 1260 | log_doc = ChatDocument( |
| 1261 | content="[CANNOT RESPOND]", |
| 1262 | metadata=ChatDocMetaData( |
| 1263 | sender=r if isinstance(r, Entity) else Entity.USER, |