applied-cli 0.5.4__tar.gz → 0.5.5__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: applied-cli
3
- Version: 0.5.4
3
+ Version: 0.5.5
4
4
  Summary: CLI and shared client library for Applied Labs AI support agents
5
5
  Author: Applied Labs
6
6
  License-Expression: MIT
@@ -4,6 +4,6 @@ from applied_cli import tools
4
4
  from applied_cli.client import AppliedClient
5
5
  from applied_cli.formatters import to_csv, to_json
6
6
 
7
- __version__ = "0.5.4"
7
+ __version__ = "0.5.5"
8
8
 
9
9
  __all__ = ["AppliedClient", "tools", "to_csv", "to_json", "__version__"]
@@ -390,3 +390,111 @@ class AppliedClient:
390
390
  params={"type": object_type, "id": object_id},
391
391
  )
392
392
  return data.get("spans", [])
393
+
394
+ # -------------------------------------------------------------------------
395
+ # Conversational Testing (via streaming /complete endpoint)
396
+ # -------------------------------------------------------------------------
397
+
398
+ async def send_message(
399
+ self,
400
+ agent_id: str,
401
+ message: str,
402
+ conversation_id: str | None = None,
403
+ contact_email: str | None = None,
404
+ contact_name: str | None = None,
405
+ contact_phone: str | None = None,
406
+ metadata: dict[str, Any] | None = None,
407
+ ) -> dict[str, Any]:
408
+ """Send a message to an agent and wait for the response.
409
+
410
+ This calls the /complete endpoint (streaming) and consumes the full
411
+ response. Use this for testing conversational flows.
412
+
413
+ Args:
414
+ agent_id: The agent UUID
415
+ message: The user message to send
416
+ conversation_id: Optional existing conversation to continue
417
+ contact_email: Optional contact email for context
418
+ contact_name: Optional contact name for context
419
+ contact_phone: Optional contact phone for context
420
+ metadata: Optional metadata dict for the conversation
421
+
422
+ Returns:
423
+ Dict with conversation_id, response_text, and status
424
+ """
425
+ headers = {
426
+ "Authorization": f"Bearer {self.token}",
427
+ "Content-Type": "application/json",
428
+ "Accept": "text/event-stream",
429
+ }
430
+ if self.shop_id:
431
+ headers["X-Shop-Id"] = self.shop_id
432
+
433
+ body: dict[str, Any] = {
434
+ "transcript": [
435
+ {
436
+ "role": "user",
437
+ "content": message,
438
+ "text": message,
439
+ "format": "markdown",
440
+ }
441
+ ],
442
+ }
443
+
444
+ if conversation_id:
445
+ body["conversation_id"] = conversation_id
446
+
447
+ # Build metadata with contact info
448
+ meta = metadata or {}
449
+ if contact_email:
450
+ meta["email"] = contact_email
451
+ if contact_name:
452
+ meta["name"] = contact_name
453
+ if contact_phone:
454
+ meta["phone"] = contact_phone
455
+ if meta:
456
+ body["metadata"] = meta
457
+
458
+ url = f"{self.base_url}/v1/agents/{agent_id}/complete/"
459
+
460
+ response_text = ""
461
+ result_conversation_id = conversation_id
462
+
463
+ async with httpx.AsyncClient(timeout=120.0) as client:
464
+ async with client.stream(
465
+ "POST", url, headers=headers, json=body
466
+ ) as response:
467
+ response.raise_for_status()
468
+
469
+ async for line in response.aiter_lines():
470
+ if not line:
471
+ continue
472
+
473
+ # Parse SSE format: "data: {...}"
474
+ if line.startswith("data: "):
475
+ data_str = line[6:] # Remove "data: " prefix
476
+ if data_str == "[DONE]":
477
+ break
478
+
479
+ try:
480
+ import json
481
+
482
+ data = json.loads(data_str)
483
+
484
+ # Extract conversation_id from first chunk
485
+ if not result_conversation_id:
486
+ result_conversation_id = data.get("conversation_id")
487
+
488
+ # Accumulate response content
489
+ if "content" in data:
490
+ response_text += data["content"]
491
+
492
+ except (json.JSONDecodeError, KeyError):
493
+ # Skip malformed chunks
494
+ pass
495
+
496
+ return {
497
+ "conversation_id": result_conversation_id,
498
+ "response": response_text,
499
+ "status": "success" if response_text else "empty",
500
+ }
@@ -845,6 +845,64 @@ async def conversation_spans(
845
845
  return result
846
846
 
847
847
 
848
+ async def send_message(
849
+ client: AppliedClient,
850
+ agent_id: str,
851
+ message: str,
852
+ conversation_id: str | None = None,
853
+ contact_email: str | None = None,
854
+ contact_name: str | None = None,
855
+ contact_phone: str | None = None,
856
+ ) -> str:
857
+ """
858
+ Send a message to an agent and get the response.
859
+
860
+ Use this to test conversational flows. After sending, you can query
861
+ the conversation messages with conversation_get for full details.
862
+
863
+ Args:
864
+ client: Authenticated AppliedClient
865
+ agent_id: The agent UUID to send the message to
866
+ message: The user message to send
867
+ conversation_id: Optional - continue an existing conversation
868
+ contact_email: Optional contact email (for flows that need it)
869
+ contact_name: Optional contact name
870
+ contact_phone: Optional contact phone
871
+
872
+ Returns:
873
+ Conversation ID, response preview, and status
874
+ """
875
+ try:
876
+ result = await client.send_message(
877
+ agent_id=agent_id,
878
+ message=message,
879
+ conversation_id=conversation_id,
880
+ contact_email=contact_email,
881
+ contact_name=contact_name,
882
+ contact_phone=contact_phone,
883
+ )
884
+
885
+ output = f"# Message Sent\n"
886
+ output += f"conversation_id: {result.get('conversation_id')}\n"
887
+ output += f"status: {result.get('status')}\n"
888
+
889
+ response = result.get("response", "")
890
+ if response:
891
+ # Truncate long responses for preview
892
+ preview = response[:500] + "..." if len(response) > 500 else response
893
+ output += f"\n## Response Preview\n{preview}\n"
894
+
895
+ output += "\n## Next Steps\n"
896
+ output += "- Use `conversation_get` with the conversation_id to see full messages\n"
897
+ output += "- Use `flow_run_list` with conversation_id to see flow runs\n"
898
+ output += "- Use `conversation_spans` for detailed execution trace\n"
899
+
900
+ return output
901
+
902
+ except Exception as e:
903
+ return f"Error sending message: {e}"
904
+
905
+
848
906
  async def executor_list(
849
907
  client: AppliedClient,
850
908
  output_format: str = "csv",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: applied-cli
3
- Version: 0.5.4
3
+ Version: 0.5.5
4
4
  Summary: CLI and shared client library for Applied Labs AI support agents
5
5
  Author: Applied Labs
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "applied-cli"
3
- version = "0.5.4"
3
+ version = "0.5.5"
4
4
  description = "CLI and shared client library for Applied Labs AI support agents"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
File without changes
File without changes