openai-sdk-helpers 0.1.2__tar.gz → 0.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/PKG-INFO +1 -1
  2. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/pyproject.toml +1 -1
  3. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/streamlit_app/app.py +122 -27
  4. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/.gitignore +0 -0
  5. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/LICENSE +0 -0
  6. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/README.md +0 -0
  7. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/__init__.py +0 -0
  8. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/__init__.py +0 -0
  9. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/base.py +0 -0
  10. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/config.py +0 -0
  11. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/coordination.py +0 -0
  12. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/prompt_utils.py +0 -0
  13. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/runner.py +0 -0
  14. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/search/__init__.py +0 -0
  15. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/search/base.py +0 -0
  16. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/search/vector.py +0 -0
  17. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/search/web.py +0 -0
  18. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/summarizer.py +0 -0
  19. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/translator.py +0 -0
  20. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/utils.py +0 -0
  21. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/agent/validation.py +0 -0
  22. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/cli.py +0 -0
  23. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/config.py +0 -0
  24. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/context_manager.py +0 -0
  25. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/deprecation.py +0 -0
  26. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/enums/__init__.py +0 -0
  27. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/enums/base.py +0 -0
  28. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/environment.py +0 -0
  29. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/errors.py +0 -0
  30. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/files_api.py +0 -0
  31. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/logging_config.py +0 -0
  32. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/prompt/__init__.py +0 -0
  33. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/prompt/base.py +0 -0
  34. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/prompt/summarizer.jinja +0 -0
  35. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/prompt/translator.jinja +0 -0
  36. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/prompt/validator.jinja +0 -0
  37. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/py.typed +0 -0
  38. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/response/__init__.py +0 -0
  39. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/response/base.py +0 -0
  40. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/response/config.py +0 -0
  41. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/response/files.py +0 -0
  42. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/response/messages.py +0 -0
  43. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/response/runner.py +0 -0
  44. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/response/tool_call.py +0 -0
  45. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/response/vector_store.py +0 -0
  46. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/retry.py +0 -0
  47. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/streamlit_app/__init__.py +0 -0
  48. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/streamlit_app/config.py +0 -0
  49. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/streamlit_app/streamlit_web_search.py +0 -0
  50. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/__init__.py +0 -0
  51. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/agent_blueprint.py +0 -0
  52. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/base.py +0 -0
  53. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/plan/__init__.py +0 -0
  54. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/plan/enum.py +0 -0
  55. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/plan/helpers.py +0 -0
  56. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/plan/plan.py +0 -0
  57. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/plan/task.py +0 -0
  58. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/plan/types.py +0 -0
  59. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/prompt.py +0 -0
  60. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/responses.py +0 -0
  61. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/summary.py +0 -0
  62. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/validation.py +0 -0
  63. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/vector_search.py +0 -0
  64. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/structure/web_search.py +0 -0
  65. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/tools.py +0 -0
  66. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/types.py +0 -0
  67. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/__init__.py +0 -0
  68. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/async_utils.py +0 -0
  69. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/coercion.py +0 -0
  70. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/deprecation.py +0 -0
  71. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/encoding.py +0 -0
  72. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/json_utils.py +0 -0
  73. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/output_validation.py +0 -0
  74. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/path_utils.py +0 -0
  75. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/utils/validation.py +0 -0
  76. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/vector_storage/__init__.py +0 -0
  77. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/vector_storage/cleanup.py +0 -0
  78. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/vector_storage/storage.py +0 -0
  79. {openai_sdk_helpers-0.1.2 → openai_sdk_helpers-0.1.4}/src/openai_sdk_helpers/vector_storage/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openai-sdk-helpers
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Composable helpers for OpenAI SDK agents, prompts, and storage
5
5
  Author: openai-sdk-helpers maintainers
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "openai-sdk-helpers"
3
- version = "0.1.2"
3
+ version = "0.1.4"
4
4
  requires-python = ">=3.10"
5
5
  readme = "README.md"
6
6
  description = "Composable helpers for OpenAI SDK agents, prompts, and storage"
@@ -8,6 +8,7 @@ rendering, response execution, and resource cleanup.
8
8
  from __future__ import annotations
9
9
 
10
10
  import json
11
+ import os
11
12
  import tempfile
12
13
  from pathlib import Path
13
14
  from typing import Any
@@ -30,22 +31,30 @@ from openai_sdk_helpers.utils import (
30
31
  log,
31
32
  )
32
33
 
33
- # Supported file extensions for OpenAI Assistants file search
34
+ # Supported file extensions for OpenAI Assistants file search and vision
34
35
  SUPPORTED_FILE_EXTENSIONS = (
35
36
  ".csv",
36
37
  ".docx",
38
+ ".gif",
37
39
  ".html",
38
40
  ".json",
41
+ ".jpeg",
42
+ ".jpg",
39
43
  ".md",
40
44
  ".pdf",
45
+ ".png",
41
46
  ".pptx",
42
47
  ".txt",
48
+ ".webp",
43
49
  ".xlsx",
44
50
  )
45
51
 
46
52
 
47
53
  def _validate_file_type(filename: str) -> bool:
48
- """Check if a file has a supported extension for vector storage.
54
+ """Check if a file has a supported extension for upload.
55
+
56
+ Supports both document formats (for file search) and image formats
57
+ (for vision analysis).
49
58
 
50
59
  Parameters
51
60
  ----------
@@ -61,6 +70,32 @@ def _validate_file_type(filename: str) -> bool:
61
70
  return file_ext in SUPPORTED_FILE_EXTENSIONS
62
71
 
63
72
 
73
+ def _cleanup_temp_files(file_paths: list[str] | None = None) -> None:
74
+ """Delete temporary files that were created for uploads.
75
+
76
+ Parameters
77
+ ----------
78
+ file_paths : list[str] or None, default None
79
+ Specific file paths to delete. If None, deletes all tracked
80
+ temporary files from session state.
81
+
82
+ Notes
83
+ -----
84
+ Silently ignores errors when deleting files that may have already
85
+ been removed or are inaccessible.
86
+ """
87
+ paths_to_delete = file_paths or st.session_state.get("temp_file_paths", [])
88
+ for path in paths_to_delete:
89
+ try:
90
+ if os.path.exists(path):
91
+ os.remove(path)
92
+ except (OSError, IOError):
93
+ pass # Silently ignore if file already deleted or inaccessible
94
+
95
+ if file_paths is None:
96
+ st.session_state["temp_file_paths"] = []
97
+
98
+
64
99
  def _extract_assistant_text(response: BaseResponse[Any]) -> str:
65
100
  """Extract the latest assistant message as readable text.
66
101
 
@@ -86,15 +121,33 @@ def _extract_assistant_text(response: BaseResponse[Any]) -> str:
86
121
  if message is None:
87
122
  return ""
88
123
 
124
+ # Check if the message content has output_text attribute
125
+ output_text = getattr(message.content, "output_text", None)
126
+ if output_text:
127
+ return str(output_text)
128
+
89
129
  content = getattr(message.content, "content", None)
90
130
  if content is None:
91
131
  return ""
92
132
 
93
133
  text_parts: list[str] = []
94
134
  for part in ensure_list(content):
95
- text_value = getattr(getattr(part, "text", None), "value", None)
96
- if text_value:
97
- text_parts.append(text_value)
135
+ # Handle both dict-like parts and object-like parts
136
+ text_content = None
137
+ if hasattr(part, "text"):
138
+ text_content = getattr(part, "text", None)
139
+ elif isinstance(part, dict):
140
+ text_content = part.get("text")
141
+
142
+ if text_content:
143
+ # If text_content is a string, use it directly (dict-style)
144
+ if isinstance(text_content, str):
145
+ text_parts.append(text_content)
146
+ # If text_content is an object with a value attribute, extract that value (object-style)
147
+ else:
148
+ text_value = getattr(text_content, "value", None)
149
+ if text_value:
150
+ text_parts.append(text_value)
98
151
  if text_parts:
99
152
  return "\n\n".join(text_parts)
100
153
  return ""
@@ -223,7 +276,8 @@ def _reset_chat(close_response: bool = True) -> None:
223
276
  """Clear conversation and optionally close the response session.
224
277
 
225
278
  Saves the current conversation to disk, closes the response to clean
226
- up resources, and clears the chat history from session state.
279
+ up resources, and clears the chat history from session state. Also
280
+ cleans up any temporary files that were created for uploads.
227
281
 
228
282
  Parameters
229
283
  ----------
@@ -234,13 +288,17 @@ def _reset_chat(close_response: bool = True) -> None:
234
288
  Notes
235
289
  -----
236
290
  This function mutates st.session_state in-place, clearing the
237
- chat_history and response_instance keys.
291
+ chat_history, response_instance, and temp_file_paths keys.
238
292
  """
239
293
  response = st.session_state.get("response_instance")
240
294
  if close_response and isinstance(response, BaseResponse):
241
295
  filepath = f"./data/{response.name}.{response.uuid}.json"
242
296
  response.save(filepath)
243
297
  response.close()
298
+
299
+ # Clean up temporary files
300
+ _cleanup_temp_files()
301
+
244
302
  st.session_state["chat_history"] = []
245
303
  st.session_state.pop("response_instance", None)
246
304
 
@@ -249,7 +307,8 @@ def _init_session_state() -> None:
249
307
  """Initialize Streamlit session state for chat functionality.
250
308
 
251
309
  Creates the chat_history list in session state if it doesn't exist,
252
- enabling conversation persistence across Streamlit reruns.
310
+ enabling conversation persistence across Streamlit reruns. Also
311
+ initializes a list for tracking temporary file paths that need cleanup.
253
312
 
254
313
  Notes
255
314
  -----
@@ -258,8 +317,12 @@ def _init_session_state() -> None:
258
317
  """
259
318
  if "chat_history" not in st.session_state:
260
319
  st.session_state["chat_history"] = []
261
- if "uploaded_files" not in st.session_state:
262
- st.session_state["uploaded_files"] = []
320
+ if "temp_file_paths" not in st.session_state:
321
+ st.session_state["temp_file_paths"] = []
322
+ if "current_attachments" not in st.session_state:
323
+ st.session_state["current_attachments"] = []
324
+ if "attachment_names" not in st.session_state:
325
+ st.session_state["attachment_names"] = []
263
326
 
264
327
 
265
328
  def _render_chat_history() -> None:
@@ -293,7 +356,10 @@ def _render_chat_history() -> None:
293
356
 
294
357
 
295
358
  def _handle_user_message(
296
- prompt: str, config: StreamlitAppConfig, attachment_paths: list[str] | None = None
359
+ prompt: str,
360
+ config: StreamlitAppConfig,
361
+ attachment_paths: list[str] | None = None,
362
+ attachment_names: list[str] | None = None,
297
363
  ) -> None:
298
364
  """Process user input and generate assistant response.
299
365
 
@@ -309,6 +375,8 @@ def _handle_user_message(
309
375
  Loaded configuration with response handler definition.
310
376
  attachment_paths : list[str] or None, default None
311
377
  Optional list of file paths to attach to the message.
378
+ attachment_names : list[str] or None, default None
379
+ Optional list of original filenames for display purposes.
312
380
 
313
381
  Notes
314
382
  -----
@@ -316,11 +384,15 @@ def _handle_user_message(
316
384
  chat transcript rather than crashing the application. The function
317
385
  triggers a Streamlit rerun after successful response generation.
318
386
  """
319
- attachment_names = (
320
- [Path(p).name for p in attachment_paths] if attachment_paths else []
387
+ # Use provided display names or fall back to extracting from paths
388
+ display_names = (
389
+ attachment_names
390
+ if attachment_names
391
+ else [Path(p).name for p in attachment_paths] if attachment_paths else []
321
392
  )
393
+
322
394
  st.session_state["chat_history"].append(
323
- {"role": "user", "content": prompt, "attachments": attachment_names}
395
+ {"role": "user", "content": prompt, "attachments": display_names}
324
396
  )
325
397
  try:
326
398
  response = _get_response_instance(config)
@@ -388,40 +460,63 @@ def main(config_path: Path) -> None:
388
460
 
389
461
  _render_chat_history()
390
462
 
391
- # File uploader for attachments
392
- uploaded_files = st.file_uploader(
393
- "Attach files (optional)",
394
- accept_multiple_files=True,
395
- key="file_uploader",
396
- help=f"Supported formats: {', '.join(sorted(SUPPORTED_FILE_EXTENSIONS))}",
397
- )
463
+ # File uploader form - auto-clears on submit
464
+ with st.form("file_upload_form", clear_on_submit=True):
465
+ uploaded_files = st.file_uploader(
466
+ "Attach files (optional)",
467
+ accept_multiple_files=True,
468
+ help=f"Supported formats: {', '.join(sorted(SUPPORTED_FILE_EXTENSIONS))}",
469
+ )
470
+ submit_files = st.form_submit_button("Attach files")
398
471
 
399
- # Save uploaded files to temporary directory and track paths
472
+ # Process uploaded files if form was submitted
400
473
  attachment_paths: list[str] = []
401
- if uploaded_files:
474
+ original_filenames: list[str] = []
475
+ if submit_files and uploaded_files:
402
476
  invalid_files = []
403
477
  for uploaded_file in uploaded_files:
404
478
  if not _validate_file_type(uploaded_file.name):
405
479
  invalid_files.append(uploaded_file.name)
406
480
  continue
481
+
482
+ # Create temporary file with the uploaded content
407
483
  with tempfile.NamedTemporaryFile(
408
484
  delete=False, suffix=Path(uploaded_file.name).suffix
409
485
  ) as tmp_file:
410
486
  tmp_file.write(uploaded_file.getbuffer())
487
+ tmp_file.flush()
411
488
  attachment_paths.append(tmp_file.name)
489
+ original_filenames.append(uploaded_file.name)
490
+ # Track for cleanup
491
+ if tmp_file.name not in st.session_state.get("temp_file_paths", []):
492
+ st.session_state["temp_file_paths"].append(tmp_file.name)
412
493
 
413
494
  if invalid_files:
414
495
  st.warning(
415
- f"⚠️ Unsupported file types skipped: {', '.join(invalid_files)}. "
416
- f"Supported formats: {', '.join(sorted(SUPPORTED_FILE_EXTENSIONS))}"
496
+ f"⚠️ Unsupported file types: {', '.join(invalid_files)}. "
497
+ f"Supported: {', '.join(sorted(SUPPORTED_FILE_EXTENSIONS))}"
417
498
  )
418
499
  if attachment_paths:
419
- st.caption(f"📎 {len(attachment_paths)} file(s) ready to attach")
500
+ st.session_state["current_attachments"] = attachment_paths
501
+ st.session_state["attachment_names"] = original_filenames
502
+ st.info(f"📎 {len(attachment_paths)} file(s) attached")
503
+
504
+ # Get attachment paths from session state if they were previously attached
505
+ attachment_paths = st.session_state.get("current_attachments", [])
506
+ attachment_display_names = st.session_state.get("attachment_names", [])
507
+ if attachment_paths:
508
+ st.caption(f"Ready to send: {', '.join(attachment_display_names)}")
420
509
 
421
510
  prompt = st.chat_input("Message the assistant")
422
511
  if prompt:
512
+ # Clear attachments before rerun to prevent them from being sent again
513
+ st.session_state["current_attachments"] = []
514
+ st.session_state["attachment_names"] = []
423
515
  _handle_user_message(
424
- prompt, config, attachment_paths if attachment_paths else None
516
+ prompt,
517
+ config,
518
+ attachment_paths or None,
519
+ attachment_display_names or None,
425
520
  )
426
521
 
427
522