trustgraph-cli 2.8.1__tar.gz → 2.8.2__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 (123) hide show
  1. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/PKG-INFO +1 -1
  2. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/dump_queues.py +36 -47
  3. trustgraph_cli-2.8.2/trustgraph/cli/monitor_prompts.py +330 -0
  4. trustgraph_cli-2.8.2/trustgraph/cli_version.py +1 -0
  5. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph_cli.egg-info/PKG-INFO +1 -1
  6. trustgraph_cli-2.8.1/trustgraph/cli/monitor_prompts.py +0 -328
  7. trustgraph_cli-2.8.1/trustgraph/cli_version.py +0 -1
  8. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/README.md +0 -0
  9. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/pyproject.toml +0 -0
  10. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/setup.cfg +0 -0
  11. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/__init__.py +0 -0
  12. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/_iam.py +0 -0
  13. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/add_library_document.py +0 -0
  14. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/bootstrap_iam.py +0 -0
  15. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/change_password.py +0 -0
  16. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/create_api_key.py +0 -0
  17. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/create_user.py +0 -0
  18. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/create_workspace.py +0 -0
  19. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/delete_collection.py +0 -0
  20. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/delete_config_item.py +0 -0
  21. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/delete_flow_blueprint.py +0 -0
  22. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/delete_kg_core.py +0 -0
  23. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/delete_mcp_tool.py +0 -0
  24. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/delete_tool.py +0 -0
  25. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/delete_user.py +0 -0
  26. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/describe_image.py +0 -0
  27. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/disable_user.py +0 -0
  28. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/dump_msgpack.py +0 -0
  29. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/enable_user.py +0 -0
  30. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/export_workspace.py +0 -0
  31. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/export_workspace_config.py +0 -0
  32. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/get_config_item.py +0 -0
  33. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/get_de_core.py +0 -0
  34. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/get_document_content.py +0 -0
  35. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/get_flow_blueprint.py +0 -0
  36. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/get_kg_core.py +0 -0
  37. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/graph_to_turtle.py +0 -0
  38. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/import_workspace.py +0 -0
  39. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/import_workspace_config.py +0 -0
  40. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/init_pulsar_manager.py +0 -0
  41. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/invoke_agent.py +0 -0
  42. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/invoke_document_embeddings.py +0 -0
  43. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/invoke_document_rag.py +0 -0
  44. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/invoke_embeddings.py +0 -0
  45. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/invoke_graph_embeddings.py +0 -0
  46. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/invoke_graph_rag.py +0 -0
  47. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/invoke_llm.py +0 -0
  48. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/invoke_mcp_tool.py +0 -0
  49. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/invoke_nlp_query.py +0 -0
  50. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/invoke_prompt.py +0 -0
  51. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/invoke_reranker.py +0 -0
  52. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/invoke_row_embeddings.py +0 -0
  53. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/invoke_rows_query.py +0 -0
  54. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/invoke_sparql_query.py +0 -0
  55. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/invoke_structured_query.py +0 -0
  56. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/list_api_keys.py +0 -0
  57. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/list_collections.py +0 -0
  58. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/list_config_items.py +0 -0
  59. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/list_explain_traces.py +0 -0
  60. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/list_my_workspaces.py +0 -0
  61. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/list_users.py +0 -0
  62. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/list_workspaces.py +0 -0
  63. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/load_doc_embeds.py +0 -0
  64. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/load_kg_core.py +0 -0
  65. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/load_knowledge.py +0 -0
  66. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/load_sample_documents.py +0 -0
  67. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/load_structured_data.py +0 -0
  68. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/load_turtle.py +0 -0
  69. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/login.py +0 -0
  70. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/nquads.py +0 -0
  71. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/put_config_item.py +0 -0
  72. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/put_de_core.py +0 -0
  73. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/put_flow_blueprint.py +0 -0
  74. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/put_kg_core.py +0 -0
  75. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/query_graph.py +0 -0
  76. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/remove_library_document.py +0 -0
  77. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/reset_password.py +0 -0
  78. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/revoke_api_key.py +0 -0
  79. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/sample_documents/__init__.py +0 -0
  80. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/sample_documents/belgian-beer.md +0 -0
  81. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/sample_documents/bronze-age-collapse.pdf +0 -0
  82. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/sample_documents/corporate-scandals.pdf +0 -0
  83. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/sample_documents/history-of-pets.md +0 -0
  84. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/sample_documents/metadata.json +0 -0
  85. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/sample_documents/mil-fortifications-america-19th-c.md +0 -0
  86. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/sample_documents/recipes.md +0 -0
  87. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/sample_documents/trade-routes-europe.md +0 -0
  88. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/save_doc_embeds.py +0 -0
  89. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/set_collection.py +0 -0
  90. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/set_mcp_tool.py +0 -0
  91. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/set_prompt.py +0 -0
  92. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/set_token_costs.py +0 -0
  93. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/set_tool.py +0 -0
  94. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_config.py +0 -0
  95. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_explain_trace.py +0 -0
  96. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_extraction_provenance.py +0 -0
  97. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_flow_blueprints.py +0 -0
  98. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_flow_state.py +0 -0
  99. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_flows.py +0 -0
  100. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_graph.py +0 -0
  101. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_kg_cores.py +0 -0
  102. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_library_documents.py +0 -0
  103. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_library_processing.py +0 -0
  104. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_mcp_tools.py +0 -0
  105. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_parameter_types.py +0 -0
  106. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_processor_state.py +0 -0
  107. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_prompts.py +0 -0
  108. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_token_costs.py +0 -0
  109. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_token_rate.py +0 -0
  110. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/show_tools.py +0 -0
  111. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/start_flow.py +0 -0
  112. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/start_library_processing.py +0 -0
  113. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/stop_flow.py +0 -0
  114. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/stop_library_processing.py +0 -0
  115. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/unload_kg_core.py +0 -0
  116. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/update_user.py +0 -0
  117. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/verify_system_status.py +0 -0
  118. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph/cli/whoami.py +0 -0
  119. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph_cli.egg-info/SOURCES.txt +0 -0
  120. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph_cli.egg-info/dependency_links.txt +0 -0
  121. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph_cli.egg-info/entry_points.txt +0 -0
  122. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph_cli.egg-info/requires.txt +0 -0
  123. {trustgraph_cli-2.8.1 → trustgraph_cli-2.8.2}/trustgraph_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trustgraph-cli
3
- Version: 2.8.1
3
+ Version: 2.8.2
4
4
  Summary: TrustGraph provides a means to run a pipeline of flexible AI processing components in a flexible means to achieve a processing pipeline.
5
5
  Author-email: "trustgraph.ai" <security@trustgraph.ai>
6
6
  Project-URL: Homepage, https://github.com/trustgraph-ai/trustgraph
@@ -1,11 +1,11 @@
1
1
  """
2
- Multi-queue Pulsar message dumper for debugging TrustGraph message flows.
2
+ Multi-queue message dumper for debugging TrustGraph message flows.
3
3
 
4
- This utility monitors multiple Pulsar queues simultaneously and logs all messages
4
+ This utility monitors multiple queues simultaneously and logs all messages
5
5
  to a file with timestamps and pretty-printed formatting. Useful for debugging
6
6
  message flows, diagnosing stuck services, and understanding system behavior.
7
7
 
8
- Uses TrustGraph's Subscriber abstraction for future-proof pub/sub compatibility.
8
+ Uses TrustGraph's async pub/sub backend for future-proof compatibility.
9
9
  """
10
10
 
11
11
  import sys
@@ -14,8 +14,7 @@ import asyncio
14
14
  from datetime import datetime
15
15
  import argparse
16
16
 
17
- from trustgraph.base.subscriber import Subscriber
18
- from trustgraph.base.pubsub import get_pubsub, add_pubsub_args
17
+ from trustgraph.base.pubsub import get_async_pubsub, add_pubsub_args
19
18
 
20
19
  def decode_json_strings(obj):
21
20
  """Recursively decode JSON-encoded string values within a dict/list."""
@@ -54,7 +53,7 @@ def to_dict(value):
54
53
  if isinstance(value, (list, tuple)):
55
54
  return [to_dict(v) for v in value]
56
55
 
57
- # Pulsar schema objects expose fields via __dict__
56
+ # Schema objects expose fields via __dict__
58
57
  if hasattr(value, '__dict__'):
59
58
  return {
60
59
  k: to_dict(v) for k, v in value.__dict__.items()
@@ -87,31 +86,27 @@ def format_message(queue_name, msg):
87
86
  return header + body + "\n"
88
87
 
89
88
 
90
- async def monitor_queue(subscriber, queue_name, central_queue, monitor_id, shutdown_event):
89
+ async def monitor_queue(consumer, queue_name, central_queue, shutdown_event):
91
90
  """
92
- Monitor a single queue via Subscriber and forward messages to central queue.
91
+ Monitor a single queue via async consumer and forward messages to central queue.
93
92
 
94
93
  Args:
95
- subscriber: Subscriber instance for this queue
94
+ consumer: Async consumer instance for this queue
96
95
  queue_name: Name of the queue (for logging)
97
96
  central_queue: asyncio.Queue to forward messages to
98
- monitor_id: Unique ID for this monitor's subscription
99
97
  shutdown_event: asyncio.Event to signal shutdown
100
98
  """
101
- msg_queue = None
102
99
  try:
103
- # Subscribe to all messages from this Subscriber
104
- msg_queue = await subscriber.subscribe_all(monitor_id)
105
-
106
100
  while not shutdown_event.is_set():
107
101
  try:
108
- # Read from Subscriber's internal queue with timeout
109
- msg = await asyncio.wait_for(msg_queue.get(), timeout=0.5)
102
+ msg = await asyncio.wait_for(consumer.receive(), timeout=0.5)
110
103
  timestamp = datetime.now()
111
104
  formatted = format_message(queue_name, msg)
112
105
 
113
106
  # Forward to central queue for writing
114
107
  await central_queue.put((timestamp, queue_name, formatted))
108
+
109
+ await consumer.acknowledge(msg)
115
110
  except asyncio.TimeoutError:
116
111
  # No message, check shutdown flag again
117
112
  continue
@@ -120,13 +115,6 @@ async def monitor_queue(subscriber, queue_name, central_queue, monitor_id, shutd
120
115
  if not shutdown_event.is_set():
121
116
  error_msg = f"\n{'='*80}\n[{datetime.now().isoformat()}] ERROR in monitor for {queue_name}\n{'='*80}\n{e}\n"
122
117
  await central_queue.put((datetime.now(), queue_name, error_msg))
123
- finally:
124
- # Clean unsubscribe
125
- if msg_queue is not None:
126
- try:
127
- await subscriber.unsubscribe_all(monitor_id)
128
- except Exception:
129
- pass
130
118
 
131
119
 
132
120
  async def log_writer(central_queue, file_handle, shutdown_event, console_output=True):
@@ -190,33 +178,31 @@ async def async_main(queues, output_file, subscriber_name, append_mode, **pubsub
190
178
 
191
179
  # Create backend connection
192
180
  try:
193
- backend = get_pubsub(**pubsub_config)
181
+ backend = get_async_pubsub(**pubsub_config)
194
182
  except Exception as e:
195
183
  print(f"Error connecting to backend: {e}", file=sys.stderr)
196
184
  sys.exit(1)
197
185
 
198
- # Create Subscribers and central queue
186
+ # Create consumers and central queue
199
187
  central_queue = asyncio.Queue()
200
- subscribers = []
188
+ consumers = []
201
189
 
202
190
  for queue_name in queues:
203
191
  try:
204
- sub = Subscriber(
205
- backend=backend,
192
+ consumer = await backend.create_consumer(
206
193
  topic=queue_name,
207
194
  subscription=subscriber_name,
208
- consumer_name=f"{subscriber_name}-{queue_name}",
209
- schema=None, # No schema - accept any message type
195
+ schema=None,
196
+ initial_position='latest',
210
197
  )
211
- await sub.start()
212
- subscribers.append((queue_name, sub))
213
- print(f"✓ Subscribed to: {queue_name}")
198
+ consumers.append((queue_name, consumer))
199
+ print(f" Subscribed to: {queue_name}")
214
200
  except Exception as e:
215
- print(f"Error subscribing to {queue_name}: {e}", file=sys.stderr)
201
+ print(f" Error subscribing to {queue_name}: {e}", file=sys.stderr)
216
202
 
217
- if not subscribers:
218
- print("\nNo subscribers created. Exiting.", file=sys.stderr)
219
- backend.close()
203
+ if not consumers:
204
+ print("\nNo consumers created. Exiting.", file=sys.stderr)
205
+ await backend.close()
220
206
  sys.exit(1)
221
207
 
222
208
  print(f"\nListening for messages...\n")
@@ -237,10 +223,10 @@ async def async_main(queues, output_file, subscriber_name, append_mode, **pubsub
237
223
  # Start monitoring tasks
238
224
  tasks = []
239
225
  try:
240
- # Create one monitor task per subscriber
241
- for queue_name, sub in subscribers:
226
+ # Create one monitor task per consumer
227
+ for queue_name, consumer in consumers:
242
228
  task = asyncio.create_task(
243
- monitor_queue(sub, queue_name, central_queue, "logger", shutdown_event)
229
+ monitor_queue(consumer, queue_name, central_queue, shutdown_event)
244
230
  )
245
231
  tasks.append(task)
246
232
 
@@ -274,17 +260,20 @@ async def async_main(queues, output_file, subscriber_name, append_mode, **pubsub
274
260
  print(f"Error writing to {output_file}: {e}", file=sys.stderr)
275
261
  sys.exit(1)
276
262
  finally:
277
- # Clean shutdown of Subscribers
278
- for _, sub in subscribers:
279
- await sub.stop()
280
- backend.close()
263
+ # Clean shutdown of consumers
264
+ for _, consumer in consumers:
265
+ try:
266
+ await consumer.close()
267
+ except Exception:
268
+ pass
269
+ await backend.close()
281
270
 
282
271
  print(f"\nMessages logged to: {output_file}")
283
272
 
284
273
  def main():
285
274
  parser = argparse.ArgumentParser(
286
275
  prog='tg-dump-queues',
287
- description='Monitor and dump messages from multiple Pulsar queues',
276
+ description='Monitor and dump messages from multiple queues',
288
277
  epilog="""
289
278
  Examples:
290
279
  # Monitor agent and prompt flow queues
@@ -308,7 +297,7 @@ IMPORTANT:
308
297
  before running this tool. If this tool subscribes first, the real services may
309
298
  encounter schema mismatch errors when they try to connect.
310
299
 
311
- Best practice: Start services Set up flows Run tg-dump-queues
300
+ Best practice: Start services -> Set up flows -> Run tg-dump-queues
312
301
  """,
313
302
  formatter_class=argparse.RawDescriptionHelpFormatter
314
303
  )
@@ -316,7 +305,7 @@ IMPORTANT:
316
305
  parser.add_argument(
317
306
  'queues',
318
307
  nargs='+',
319
- help='Pulsar queue names to monitor'
308
+ help='Queue names to monitor'
320
309
  )
321
310
 
322
311
  parser.add_argument(
@@ -0,0 +1,330 @@
1
+ """
2
+ Monitor prompt request/response queues and log activity with timing.
3
+
4
+ Subscribes to prompt request and response queues, correlates
5
+ them by message ID, and logs a summary of each request/response with
6
+ elapsed time. Streaming responses are accumulated and shown once at
7
+ completion.
8
+
9
+ Examples:
10
+ tg-monitor-prompts
11
+ tg-monitor-prompts --flow default --max-lines 5
12
+ tg-monitor-prompts --queue-type prompt-rag
13
+ """
14
+
15
+ import json
16
+ import asyncio
17
+ import sys
18
+ import argparse
19
+ from datetime import datetime
20
+ from collections import OrderedDict
21
+
22
+ from trustgraph.base.pubsub import get_async_pubsub, add_pubsub_args
23
+
24
+
25
+ default_flow = "default"
26
+ default_queue_type = "prompt-rag"
27
+ default_max_lines = 3
28
+ default_max_width = 80
29
+
30
+
31
+ def truncate_text(text, max_lines, max_width):
32
+ """Truncate text to max_lines lines, each at most max_width chars."""
33
+ if not text:
34
+ return "(empty)"
35
+
36
+ lines = text.splitlines()
37
+ result = []
38
+ for line in lines[:max_lines]:
39
+ if len(line) > max_width:
40
+ result.append(line[:max_width - 3] + "...")
41
+ else:
42
+ result.append(line)
43
+
44
+ if len(lines) > max_lines:
45
+ result.append(f" ... ({len(lines) - max_lines} more lines)")
46
+
47
+ return "\n".join(result)
48
+
49
+
50
+ def summarise_value(value, max_width):
51
+ """Summarise a term value -- show type and size for large values."""
52
+ # Try to parse JSON
53
+ try:
54
+ parsed = json.loads(value)
55
+ except (json.JSONDecodeError, TypeError):
56
+ parsed = value
57
+
58
+ if isinstance(parsed, list):
59
+ return f"[{len(parsed)} items]"
60
+ elif isinstance(parsed, dict):
61
+ return f"{{{len(parsed)} keys}}"
62
+ elif isinstance(parsed, str):
63
+ if len(parsed) > max_width:
64
+ return parsed[:max_width - 3] + "..."
65
+ return parsed
66
+ else:
67
+ s = str(parsed)
68
+ if len(s) > max_width:
69
+ return s[:max_width - 3] + "..."
70
+ return s
71
+
72
+
73
+ def format_terms(terms, max_lines, max_width):
74
+ """Format prompt terms for display -- concise summary."""
75
+ if not terms:
76
+ return ""
77
+
78
+ parts = []
79
+ for key, value in terms.items():
80
+ summary = summarise_value(value, max_width - len(key) - 4)
81
+ parts.append(f" {key}: {summary}")
82
+
83
+ return "\n".join(parts)
84
+
85
+
86
+ def parse_raw_message(msg):
87
+ """Parse a raw message into (correlation_id, body_dict)."""
88
+ try:
89
+ props = msg.properties()
90
+ corr_id = props.get("id", "")
91
+ except Exception:
92
+ corr_id = ""
93
+
94
+ try:
95
+ value = msg.value()
96
+ if isinstance(value, dict):
97
+ body = value
98
+ elif isinstance(value, bytes):
99
+ body = json.loads(value.decode("utf-8"))
100
+ elif isinstance(value, str):
101
+ body = json.loads(value)
102
+ else:
103
+ body = {}
104
+ except Exception:
105
+ body = {}
106
+
107
+ return corr_id, body
108
+
109
+
110
+ async def poll_consumer(consumer, queue_label, handler, shutdown_event):
111
+ """Poll a single consumer and call handler for each message."""
112
+ while not shutdown_event.is_set():
113
+ try:
114
+ msg = await asyncio.wait_for(consumer.receive(), timeout=0.1)
115
+ await handler(msg)
116
+ await consumer.acknowledge(msg)
117
+ except asyncio.TimeoutError:
118
+ continue
119
+
120
+
121
+ async def monitor(flow, queue_type, max_lines, max_width, **config):
122
+
123
+ request_queue = f"request:tg:{queue_type}:{flow}"
124
+ response_queue = f"response:tg:{queue_type}:{flow}"
125
+
126
+ print(f"Monitoring prompt queues:")
127
+ print(f" Request: {request_queue}")
128
+ print(f" Response: {response_queue}")
129
+ print(f"Press Ctrl+C to stop\n")
130
+
131
+ backend = get_async_pubsub(**config)
132
+
133
+ req_consumer = await backend.create_consumer(
134
+ topic=request_queue,
135
+ subscription="prompt-monitor-req",
136
+ schema=None,
137
+ initial_position='latest',
138
+ )
139
+
140
+ resp_consumer = await backend.create_consumer(
141
+ topic=response_queue,
142
+ subscription="prompt-monitor-resp",
143
+ schema=None,
144
+ initial_position='latest',
145
+ )
146
+
147
+ # Track in-flight requests: corr_id -> (timestamp, template_id)
148
+ in_flight = OrderedDict()
149
+
150
+ # Accumulate streaming responses: corr_id -> list of text chunks
151
+ streaming_chunks = {}
152
+
153
+ print("Listening...\n")
154
+
155
+ shutdown_event = asyncio.Event()
156
+
157
+ async def handle_request(msg):
158
+ timestamp = datetime.now()
159
+ corr_id, body = parse_raw_message(msg)
160
+ time_str = timestamp.strftime("%H:%M:%S.%f")[:-3]
161
+
162
+ template_id = body.get("id", "(unknown)")
163
+ terms = body.get("terms", {})
164
+ streaming = body.get("streaming", False)
165
+
166
+ in_flight[corr_id] = (timestamp, template_id)
167
+
168
+ # Limit size
169
+ while len(in_flight) > 1000:
170
+ in_flight.popitem(last=False)
171
+
172
+ stream_flag = " [streaming]" if streaming else ""
173
+ id_display = corr_id[:8] if corr_id else "--------"
174
+ print(f"[{time_str}] REQ {id_display} "
175
+ f"template={template_id}{stream_flag}")
176
+
177
+ if terms:
178
+ print(format_terms(terms, max_lines, max_width))
179
+
180
+ async def handle_response(msg):
181
+ timestamp = datetime.now()
182
+ corr_id, body = parse_raw_message(msg)
183
+ time_str = timestamp.strftime("%H:%M:%S.%f")[:-3]
184
+ id_display = corr_id[:8] if corr_id else "--------"
185
+
186
+ error = body.get("error")
187
+ text = body.get("text", "")
188
+ obj = body.get("object", "")
189
+ eos = body.get("end_of_stream", False)
190
+
191
+ if error:
192
+ # Error -- show immediately
193
+ elapsed_str = ""
194
+ if corr_id in in_flight:
195
+ req_timestamp, _ = in_flight.pop(corr_id)
196
+ elapsed = (timestamp - req_timestamp).total_seconds()
197
+ elapsed_str = f" ({elapsed:.3f}s)"
198
+ streaming_chunks.pop(corr_id, None)
199
+
200
+ err_msg = error
201
+ if isinstance(error, dict):
202
+ err_msg = error.get("message", str(error))
203
+ print(f"[{time_str}] ERR {id_display} "
204
+ f"{err_msg}{elapsed_str}")
205
+
206
+ elif eos:
207
+ # End of stream -- show accumulated text + timing
208
+ elapsed_str = ""
209
+ if corr_id in in_flight:
210
+ req_timestamp, _ = in_flight.pop(corr_id)
211
+ elapsed = (timestamp - req_timestamp).total_seconds()
212
+ elapsed_str = f" ({elapsed:.3f}s)"
213
+
214
+ accumulated = streaming_chunks.pop(corr_id, [])
215
+ if text:
216
+ accumulated.append(text)
217
+
218
+ full_text = "".join(accumulated)
219
+ if full_text:
220
+ truncated = truncate_text(
221
+ full_text, max_lines, max_width
222
+ )
223
+ print(f"[{time_str}] RESP {id_display}"
224
+ f"{elapsed_str}")
225
+ print(f" {truncated}")
226
+ else:
227
+ print(f"[{time_str}] RESP {id_display}"
228
+ f"{elapsed_str} (empty)")
229
+
230
+ elif text or obj:
231
+ # Streaming chunk or non-streaming response
232
+ if corr_id in streaming_chunks or (
233
+ corr_id in in_flight
234
+ ):
235
+ # Accumulate streaming chunk
236
+ if corr_id not in streaming_chunks:
237
+ streaming_chunks[corr_id] = []
238
+ streaming_chunks[corr_id].append(text or obj)
239
+ else:
240
+ # Non-streaming single response
241
+ elapsed_str = ""
242
+ if corr_id in in_flight:
243
+ req_timestamp, _ = in_flight.pop(corr_id)
244
+ elapsed = (
245
+ timestamp - req_timestamp
246
+ ).total_seconds()
247
+ elapsed_str = f" ({elapsed:.3f}s)"
248
+
249
+ content = text or obj
250
+ label = "" if text else " (object)"
251
+ truncated = truncate_text(
252
+ content, max_lines, max_width
253
+ )
254
+ print(f"[{time_str}] RESP {id_display}"
255
+ f"{label}{elapsed_str}")
256
+ print(f" {truncated}")
257
+
258
+ try:
259
+ await asyncio.gather(
260
+ poll_consumer(
261
+ req_consumer, "request", handle_request, shutdown_event,
262
+ ),
263
+ poll_consumer(
264
+ resp_consumer, "response", handle_response, shutdown_event,
265
+ ),
266
+ )
267
+ except KeyboardInterrupt:
268
+ print("\nStopping...")
269
+ finally:
270
+ shutdown_event.set()
271
+ await req_consumer.close()
272
+ await resp_consumer.close()
273
+ await backend.close()
274
+
275
+
276
+ def main():
277
+ parser = argparse.ArgumentParser(
278
+ prog="tg-monitor-prompts",
279
+ description=__doc__,
280
+ formatter_class=argparse.RawDescriptionHelpFormatter,
281
+ )
282
+
283
+ parser.add_argument(
284
+ "-f", "--flow",
285
+ default=default_flow,
286
+ help=f"Flow ID (default: {default_flow})",
287
+ )
288
+
289
+ parser.add_argument(
290
+ "-q", "--queue-type",
291
+ default=default_queue_type,
292
+ help=f"Queue type: prompt or prompt-rag (default: {default_queue_type})",
293
+ )
294
+
295
+ parser.add_argument(
296
+ "-l", "--max-lines",
297
+ type=int,
298
+ default=default_max_lines,
299
+ help=f"Max lines of text per term/response (default: {default_max_lines})",
300
+ )
301
+
302
+ parser.add_argument(
303
+ "-w", "--max-width",
304
+ type=int,
305
+ default=default_max_width,
306
+ help=f"Max width per line (default: {default_max_width})",
307
+ )
308
+
309
+ add_pubsub_args(parser, standalone=True)
310
+
311
+ args = parser.parse_args()
312
+
313
+ try:
314
+ asyncio.run(monitor(
315
+ flow=args.flow,
316
+ queue_type=args.queue_type,
317
+ max_lines=args.max_lines,
318
+ max_width=args.max_width,
319
+ **{k: v for k, v in vars(args).items()
320
+ if k not in ('flow', 'queue_type', 'max_lines', 'max_width')},
321
+ ))
322
+ except KeyboardInterrupt:
323
+ pass
324
+ except Exception as e:
325
+ print(f"Fatal error: {e}", file=sys.stderr)
326
+ sys.exit(1)
327
+
328
+
329
+ if __name__ == "__main__":
330
+ main()
@@ -0,0 +1 @@
1
+ __version__ = "2.8.2"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trustgraph-cli
3
- Version: 2.8.1
3
+ Version: 2.8.2
4
4
  Summary: TrustGraph provides a means to run a pipeline of flexible AI processing components in a flexible means to achieve a processing pipeline.
5
5
  Author-email: "trustgraph.ai" <security@trustgraph.ai>
6
6
  Project-URL: Homepage, https://github.com/trustgraph-ai/trustgraph