Send user message and stream Claude's response. Args: user_message: The user's response attachments: Optional list of image attachments Yields: Message chunks of various types: - {"type": "text", "content": str} -
(
self,
user_message: str,
attachments: list[ImageAttachment] | None = None
)
| 199 | } |
| 200 | |
| 201 | async def send_message( |
| 202 | self, |
| 203 | user_message: str, |
| 204 | attachments: list[ImageAttachment] | None = None |
| 205 | ) -> AsyncGenerator[dict, None]: |
| 206 | """ |
| 207 | Send user message and stream Claude's response. |
| 208 | |
| 209 | Args: |
| 210 | user_message: The user's response |
| 211 | attachments: Optional list of image attachments |
| 212 | |
| 213 | Yields: |
| 214 | Message chunks of various types: |
| 215 | - {"type": "text", "content": str} |
| 216 | - {"type": "question", "questions": list} |
| 217 | - {"type": "spec_complete", "path": str} |
| 218 | - {"type": "error", "content": str} |
| 219 | """ |
| 220 | if not self.client: |
| 221 | yield { |
| 222 | "type": "error", |
| 223 | "content": "Session not initialized. Call start() first." |
| 224 | } |
| 225 | return |
| 226 | |
| 227 | # Store the user message |
| 228 | self.messages.append({ |
| 229 | "role": "user", |
| 230 | "content": user_message, |
| 231 | "has_attachments": bool(attachments), |
| 232 | "timestamp": datetime.now().isoformat() |
| 233 | }) |
| 234 | |
| 235 | try: |
| 236 | async for chunk in self._query_claude(user_message, attachments): |
| 237 | yield chunk |
| 238 | # Signal that the response is complete (for UI to hide loading indicator) |
| 239 | yield {"type": "response_done"} |
| 240 | except Exception as e: |
| 241 | logger.exception("Error during Claude query") |
| 242 | yield { |
| 243 | "type": "error", |
| 244 | "content": f"Error: {str(e)}" |
| 245 | } |
| 246 | |
| 247 | async def _query_claude( |
| 248 | self, |
no test coverage detected