agentex-sdk 0.2.5__py3-none-any.whl → 0.2.7__py3-none-any.whl

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 (47) hide show
  1. agentex/_base_client.py +4 -1
  2. agentex/_files.py +4 -4
  3. agentex/_version.py +1 -1
  4. agentex/lib/adk/_modules/acp.py +2 -2
  5. agentex/lib/adk/_modules/agent_task_tracker.py +2 -2
  6. agentex/lib/adk/_modules/agents.py +2 -2
  7. agentex/lib/adk/_modules/events.py +2 -2
  8. agentex/lib/adk/_modules/messages.py +2 -2
  9. agentex/lib/adk/_modules/state.py +2 -2
  10. agentex/lib/adk/_modules/streaming.py +2 -2
  11. agentex/lib/adk/_modules/tasks.py +2 -2
  12. agentex/lib/adk/_modules/tracing.py +2 -2
  13. agentex/lib/adk/providers/_modules/litellm.py +2 -1
  14. agentex/lib/adk/providers/_modules/openai.py +2 -1
  15. agentex/lib/adk/providers/_modules/sgp.py +2 -1
  16. agentex/lib/adk/utils/_modules/client.py +21 -35
  17. agentex/lib/adk/utils/_modules/templating.py +2 -1
  18. agentex/lib/cli/commands/agents.py +36 -2
  19. agentex/lib/cli/debug/__init__.py +15 -0
  20. agentex/lib/cli/debug/debug_config.py +116 -0
  21. agentex/lib/cli/debug/debug_handlers.py +174 -0
  22. agentex/lib/cli/handlers/agent_handlers.py +3 -2
  23. agentex/lib/cli/handlers/deploy_handlers.py +1 -2
  24. agentex/lib/cli/handlers/run_handlers.py +24 -7
  25. agentex/lib/cli/templates/default/pyproject.toml.j2 +0 -1
  26. agentex/lib/cli/templates/sync/pyproject.toml.j2 +0 -1
  27. agentex/lib/cli/templates/temporal/project/acp.py.j2 +31 -0
  28. agentex/lib/cli/templates/temporal/project/run_worker.py.j2 +4 -1
  29. agentex/lib/cli/templates/temporal/pyproject.toml.j2 +1 -1
  30. agentex/lib/core/services/adk/acp/acp.py +5 -5
  31. agentex/lib/core/temporal/activities/__init__.py +2 -1
  32. agentex/lib/core/temporal/workers/worker.py +24 -0
  33. agentex/lib/core/tracing/processors/agentex_tracing_processor.py +2 -1
  34. agentex/lib/environment_variables.py +7 -1
  35. agentex/lib/sdk/fastacp/base/base_acp_server.py +13 -89
  36. agentex/lib/sdk/fastacp/impl/agentic_base_acp.py +3 -1
  37. agentex/lib/utils/debug.py +63 -0
  38. agentex/lib/utils/registration.py +101 -0
  39. agentex/resources/tasks.py +54 -4
  40. agentex/types/__init__.py +1 -0
  41. agentex/types/agent.py +7 -0
  42. agentex/types/task_list_params.py +14 -0
  43. {agentex_sdk-0.2.5.dist-info → agentex_sdk-0.2.7.dist-info}/METADATA +32 -1
  44. {agentex_sdk-0.2.5.dist-info → agentex_sdk-0.2.7.dist-info}/RECORD +47 -41
  45. {agentex_sdk-0.2.5.dist-info → agentex_sdk-0.2.7.dist-info}/WHEEL +0 -0
  46. {agentex_sdk-0.2.5.dist-info → agentex_sdk-0.2.7.dist-info}/entry_points.txt +0 -0
  47. {agentex_sdk-0.2.5.dist-info → agentex_sdk-0.2.7.dist-info}/licenses/LICENSE +0 -0
@@ -1,15 +1,10 @@
1
1
  import asyncio
2
- import base64
3
2
  import inspect
4
- import json
5
- import os
6
3
  from collections.abc import AsyncGenerator, Awaitable, Callable
7
4
  from contextlib import asynccontextmanager
8
5
  from typing import Any
9
6
 
10
- import httpx
11
7
  import uvicorn
12
- from agentex.lib.adk.utils._modules.client import get_async_agentex_client
13
8
  from fastapi import FastAPI, Request
14
9
  from fastapi.responses import StreamingResponse
15
10
  from pydantic import TypeAdapter, ValidationError
@@ -30,6 +25,7 @@ from agentex.lib.types.task_message_updates import StreamTaskMessageFull, TaskMe
30
25
  from agentex.types.task_message_content import TaskMessageContent
31
26
  from agentex.lib.utils.logging import make_logger
32
27
  from agentex.lib.utils.model_utils import BaseModel
28
+ from agentex.lib.utils.registration import register_agent
33
29
 
34
30
  logger = make_logger(__name__)
35
31
 
@@ -74,7 +70,7 @@ class BaseACPServer(FastAPI):
74
70
  async def lifespan_context(app: FastAPI):
75
71
  env_vars = EnvironmentVariables.refresh()
76
72
  if env_vars.AGENTEX_BASE_URL:
77
- await self._register_agent(env_vars)
73
+ await register_agent(env_vars)
78
74
  else:
79
75
  logger.warning("AGENTEX_BASE_URL not set, skipping agent registration")
80
76
 
@@ -101,6 +97,16 @@ class BaseACPServer(FastAPI):
101
97
  data = await request.json()
102
98
  rpc_request = JSONRPCRequest(**data)
103
99
 
100
+ # Check if the request is authenticated
101
+ if refreshed_environment_variables and getattr(refreshed_environment_variables, "AGENT_API_KEY", None):
102
+ authorization_header = request.headers.get("x-agent-api-key")
103
+ if authorization_header != refreshed_environment_variables.AGENT_API_KEY:
104
+ return JSONRPCResponse(
105
+ id=rpc_request.id,
106
+ error=JSONRPCError(code=-32601, message="Unauthorized"),
107
+ )
108
+
109
+
104
110
  # Check if method is valid first
105
111
  try:
106
112
  method = RPCMethod(rpc_request.method)
@@ -345,86 +351,4 @@ class BaseACPServer(FastAPI):
345
351
  """Start the Uvicorn server for async handlers."""
346
352
  uvicorn.run(self, host=host, port=port, **kwargs)
347
353
 
348
- def _get_auth_principal(self, env_vars: EnvironmentVariables):
349
- if not env_vars.AUTH_PRINCIPAL_B64:
350
- return None
351
-
352
- try:
353
- decoded_str = base64.b64decode(env_vars.AUTH_PRINCIPAL_B64).decode('utf-8')
354
- return json.loads(decoded_str)
355
- except Exception:
356
- return None
357
-
358
- async def _register_agent(self, env_vars: EnvironmentVariables):
359
- """Register this agent with the Agentex server"""
360
- # Build the agent's own URL
361
- full_acp_url = f"{env_vars.ACP_URL.rstrip('/')}:{env_vars.ACP_PORT}"
362
-
363
- description = (
364
- env_vars.AGENT_DESCRIPTION
365
- or f"Generic description for agent: {env_vars.AGENT_NAME}"
366
- )
367
-
368
- # Prepare registration data
369
- registration_data = {
370
- "name": env_vars.AGENT_NAME,
371
- "description": description,
372
- "acp_url": full_acp_url,
373
- "acp_type": env_vars.ACP_TYPE,
374
- "principal_context": self._get_auth_principal(env_vars)
375
- }
376
-
377
- if env_vars.AGENT_ID:
378
- registration_data["agent_id"] = env_vars.AGENT_ID
379
-
380
- # Make the registration request
381
- registration_url = f"{env_vars.AGENTEX_BASE_URL.rstrip('/')}/agents/register"
382
- # Retry logic with configurable attempts and delay
383
- max_retries = 3
384
- base_delay = 5 # seconds
385
- last_exception = None
386
-
387
- attempt = 0
388
- while attempt < max_retries:
389
- try:
390
- async with httpx.AsyncClient() as client:
391
- response = await client.post(
392
- registration_url, json=registration_data, timeout=30.0
393
- )
394
- if response.status_code == 200:
395
- agent = response.json()
396
- agent_id, agent_name = agent["id"], agent["name"]
397
-
398
- os.environ["AGENT_ID"] = agent_id
399
- os.environ["AGENT_NAME"] = agent_name
400
- refreshed_environment_variables.AGENT_ID = agent_id
401
- refreshed_environment_variables.AGENT_NAME = agent_name
402
- get_async_agentex_client() # refresh cache
403
- logger.info(
404
- f"Successfully registered agent '{env_vars.AGENT_NAME}' with Agentex server with acp_url: {full_acp_url}. Registration data: {registration_data}"
405
- )
406
- return # Success, exit the retry loop
407
- else:
408
- error_msg = f"Failed to register agent. Status: {response.status_code}, Response: {response.text}"
409
- logger.error(error_msg)
410
- last_exception = Exception(
411
- f"Failed to startup agent: {response.text}"
412
- )
413
-
414
- except Exception as e:
415
- logger.error(
416
- f"Exception during agent registration attempt {attempt + 1}: {e}"
417
- )
418
- last_exception = e
419
- attempt += 1
420
- if attempt < max_retries:
421
- delay = (attempt) * base_delay # 5, 10, 15 seconds
422
- logger.info(
423
- f"Retrying in {delay} seconds... (attempt {attempt}/{max_retries})"
424
- )
425
- await asyncio.sleep(delay)
426
-
427
- # If we get here, all retries failed
428
- raise last_exception or Exception(
429
- f"Failed to register agent after {max_retries} attempts"
430
- )
354
+
@@ -1,4 +1,6 @@
1
1
  from typing import Any
2
+
3
+ from agentex.lib.adk.utils._modules.client import create_async_agentex_client
2
4
  from typing_extensions import override
3
5
  from agentex import AsyncAgentex
4
6
  from agentex.lib.sdk.fastacp.base.base_acp_server import BaseACPServer
@@ -24,7 +26,7 @@ class AgenticBaseACP(BaseACPServer):
24
26
  def __init__(self):
25
27
  super().__init__()
26
28
  self._setup_handlers()
27
- self._agentex_client = AsyncAgentex()
29
+ self._agentex_client = create_async_agentex_client()
28
30
 
29
31
  @classmethod
30
32
  @override
@@ -0,0 +1,63 @@
1
+ """
2
+ Debug utilities for AgentEx development.
3
+
4
+ Provides debugging setup functionality that can be used across different components.
5
+ """
6
+
7
+ import os
8
+ import debugpy # type: ignore
9
+
10
+
11
+ def setup_debug_if_enabled() -> None:
12
+ """
13
+ Setup debugpy if debug mode is enabled via environment variables.
14
+
15
+ This function checks for AgentEx debug environment variables and configures
16
+ debugpy accordingly. It's designed to be called early in worker startup.
17
+
18
+ Environment Variables:
19
+ AGENTEX_DEBUG_ENABLED: Set to "true" to enable debug mode
20
+ AGENTEX_DEBUG_PORT: Port for debug server (default: 5678)
21
+ AGENTEX_DEBUG_TYPE: Type identifier for logging (default: "worker")
22
+ AGENTEX_DEBUG_WAIT_FOR_ATTACH: Set to "true" to wait for debugger attachment
23
+
24
+ Raises:
25
+ Any exception from debugpy setup (will bubble up naturally)
26
+ """
27
+ if os.getenv("AGENTEX_DEBUG_ENABLED") == "true":
28
+ debug_port = int(os.getenv("AGENTEX_DEBUG_PORT", "5678"))
29
+ debug_type = os.getenv("AGENTEX_DEBUG_TYPE", "worker")
30
+ wait_for_attach = os.getenv("AGENTEX_DEBUG_WAIT_FOR_ATTACH", "false").lower() == "true"
31
+
32
+ # Configure debugpy
33
+ debugpy.configure(subProcess=False)
34
+ debugpy.listen(debug_port)
35
+
36
+ print(f"🐛 [{debug_type.upper()}] Debug server listening on port {debug_port}")
37
+
38
+ if wait_for_attach:
39
+ print(f"⏳ [{debug_type.upper()}] Waiting for debugger to attach...")
40
+ debugpy.wait_for_client()
41
+ print(f"✅ [{debug_type.upper()}] Debugger attached!")
42
+ else:
43
+ print(f"📡 [{debug_type.upper()}] Ready for debugger attachment")
44
+
45
+
46
+ def is_debug_enabled() -> bool:
47
+ """
48
+ Check if debug mode is currently enabled.
49
+
50
+ Returns:
51
+ bool: True if AGENTEX_DEBUG_ENABLED is set to "true"
52
+ """
53
+ return os.getenv("AGENTEX_DEBUG_ENABLED", "false").lower() == "true"
54
+
55
+
56
+ def get_debug_port() -> int:
57
+ """
58
+ Get the debug port from environment variables.
59
+
60
+ Returns:
61
+ int: Debug port (default: 5678)
62
+ """
63
+ return int(os.getenv("AGENTEX_DEBUG_PORT", "5678"))
@@ -0,0 +1,101 @@
1
+ import base64
2
+ import json
3
+ import os
4
+ import httpx
5
+ import asyncio
6
+
7
+ from agentex.lib.environment_variables import EnvironmentVariables, refreshed_environment_variables
8
+ from agentex.lib.utils.logging import make_logger
9
+
10
+ logger = make_logger(__name__)
11
+
12
+ def get_auth_principal(env_vars: EnvironmentVariables):
13
+ if not env_vars.AUTH_PRINCIPAL_B64:
14
+ return None
15
+
16
+ try:
17
+ decoded_str = base64.b64decode(env_vars.AUTH_PRINCIPAL_B64).decode('utf-8')
18
+ return json.loads(decoded_str)
19
+ except Exception:
20
+ return None
21
+
22
+ async def register_agent(env_vars: EnvironmentVariables):
23
+ """Register this agent with the Agentex server"""
24
+ if not env_vars.AGENTEX_BASE_URL:
25
+ logger.warning("AGENTEX_BASE_URL is not set, skipping registration")
26
+ return
27
+ # Build the agent's own URL
28
+ full_acp_url = f"{env_vars.ACP_URL.rstrip('/')}:{env_vars.ACP_PORT}"
29
+
30
+ description = (
31
+ env_vars.AGENT_DESCRIPTION
32
+ or f"Generic description for agent: {env_vars.AGENT_NAME}"
33
+ )
34
+
35
+ # Prepare registration data
36
+ registration_data = {
37
+ "name": env_vars.AGENT_NAME,
38
+ "description": description,
39
+ "acp_url": full_acp_url,
40
+ "acp_type": env_vars.ACP_TYPE,
41
+ "principal_context": get_auth_principal(env_vars)
42
+ }
43
+
44
+ if env_vars.AGENT_ID:
45
+ registration_data["agent_id"] = env_vars.AGENT_ID
46
+
47
+ # Make the registration request
48
+ registration_url = f"{env_vars.AGENTEX_BASE_URL.rstrip('/')}/agents/register"
49
+ # Retry logic with configurable attempts and delay
50
+ max_retries = 3
51
+ base_delay = 5 # seconds
52
+ last_exception = None
53
+
54
+ attempt = 0
55
+ while attempt < max_retries:
56
+ try:
57
+ async with httpx.AsyncClient() as client:
58
+ response = await client.post(
59
+ registration_url, json=registration_data, timeout=30.0
60
+ )
61
+ if response.status_code == 200:
62
+ agent = response.json()
63
+ agent_id, agent_name = agent["id"], agent["name"]
64
+ agent_api_key = agent["agent_api_key"]
65
+
66
+ os.environ["AGENT_ID"] = agent_id
67
+ os.environ["AGENT_NAME"] = agent_name
68
+ os.environ["AGENT_API_KEY"] = agent_api_key
69
+ env_vars.AGENT_ID = agent_id
70
+ env_vars.AGENT_NAME = agent_name
71
+ env_vars.AGENT_API_KEY = agent_api_key
72
+ global refreshed_environment_variables
73
+ refreshed_environment_variables = env_vars
74
+ logger.info(
75
+ f"Successfully registered agent '{env_vars.AGENT_NAME}' with Agentex server with acp_url: {full_acp_url}. Registration data: {registration_data}"
76
+ )
77
+ return # Success, exit the retry loop
78
+ else:
79
+ error_msg = f"Failed to register agent. Status: {response.status_code}, Response: {response.text}"
80
+ logger.error(error_msg)
81
+ last_exception = Exception(
82
+ f"Failed to startup agent: {response.text}"
83
+ )
84
+
85
+ except Exception as e:
86
+ logger.error(
87
+ f"Exception during agent registration attempt {attempt + 1}: {e}"
88
+ )
89
+ last_exception = e
90
+ attempt += 1
91
+ if attempt < max_retries:
92
+ delay = (attempt) * base_delay # 5, 10, 15 seconds
93
+ logger.info(
94
+ f"Retrying in {delay} seconds... (attempt {attempt}/{max_retries})"
95
+ )
96
+ await asyncio.sleep(delay)
97
+
98
+ # If we get here, all retries failed
99
+ raise last_exception or Exception(
100
+ f"Failed to register agent after {max_retries} attempts"
101
+ )
@@ -2,9 +2,13 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from typing import Optional
6
+
5
7
  import httpx
6
8
 
9
+ from ..types import task_list_params
7
10
  from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
11
+ from .._utils import maybe_transform, async_maybe_transform
8
12
  from .._compat import cached_property
9
13
  from .._resource import SyncAPIResource, AsyncAPIResource
10
14
  from .._response import (
@@ -78,6 +82,8 @@ class TasksResource(SyncAPIResource):
78
82
  def list(
79
83
  self,
80
84
  *,
85
+ agent_id: Optional[str] | NotGiven = NOT_GIVEN,
86
+ agent_name: Optional[str] | NotGiven = NOT_GIVEN,
81
87
  # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
82
88
  # The extra values given here take precedence over values defined on the client or passed to this method.
83
89
  extra_headers: Headers | None = None,
@@ -85,11 +91,32 @@ class TasksResource(SyncAPIResource):
85
91
  extra_body: Body | None = None,
86
92
  timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
87
93
  ) -> TaskListResponse:
88
- """List all tasks."""
94
+ """
95
+ List all tasks.
96
+
97
+ Args:
98
+ extra_headers: Send extra headers
99
+
100
+ extra_query: Add additional query parameters to the request
101
+
102
+ extra_body: Add additional JSON properties to the request
103
+
104
+ timeout: Override the client-level default timeout for this request, in seconds
105
+ """
89
106
  return self._get(
90
107
  "/tasks",
91
108
  options=make_request_options(
92
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
109
+ extra_headers=extra_headers,
110
+ extra_query=extra_query,
111
+ extra_body=extra_body,
112
+ timeout=timeout,
113
+ query=maybe_transform(
114
+ {
115
+ "agent_id": agent_id,
116
+ "agent_name": agent_name,
117
+ },
118
+ task_list_params.TaskListParams,
119
+ ),
93
120
  ),
94
121
  cast_to=TaskListResponse,
95
122
  )
@@ -320,6 +347,8 @@ class AsyncTasksResource(AsyncAPIResource):
320
347
  async def list(
321
348
  self,
322
349
  *,
350
+ agent_id: Optional[str] | NotGiven = NOT_GIVEN,
351
+ agent_name: Optional[str] | NotGiven = NOT_GIVEN,
323
352
  # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
324
353
  # The extra values given here take precedence over values defined on the client or passed to this method.
325
354
  extra_headers: Headers | None = None,
@@ -327,11 +356,32 @@ class AsyncTasksResource(AsyncAPIResource):
327
356
  extra_body: Body | None = None,
328
357
  timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
329
358
  ) -> TaskListResponse:
330
- """List all tasks."""
359
+ """
360
+ List all tasks.
361
+
362
+ Args:
363
+ extra_headers: Send extra headers
364
+
365
+ extra_query: Add additional query parameters to the request
366
+
367
+ extra_body: Add additional JSON properties to the request
368
+
369
+ timeout: Override the client-level default timeout for this request, in seconds
370
+ """
331
371
  return await self._get(
332
372
  "/tasks",
333
373
  options=make_request_options(
334
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
374
+ extra_headers=extra_headers,
375
+ extra_query=extra_query,
376
+ extra_body=extra_body,
377
+ timeout=timeout,
378
+ query=await async_maybe_transform(
379
+ {
380
+ "agent_id": agent_id,
381
+ "agent_name": agent_name,
382
+ },
383
+ task_list_params.TaskListParams,
384
+ ),
335
385
  ),
336
386
  cast_to=TaskListResponse,
337
387
  )
agentex/types/__init__.py CHANGED
@@ -19,6 +19,7 @@ from .message_author import MessageAuthor as MessageAuthor
19
19
  from .agent_rpc_params import AgentRpcParams as AgentRpcParams
20
20
  from .agent_rpc_result import AgentRpcResult as AgentRpcResult
21
21
  from .span_list_params import SpanListParams as SpanListParams
22
+ from .task_list_params import TaskListParams as TaskListParams
22
23
  from .agent_list_params import AgentListParams as AgentListParams
23
24
  from .event_list_params import EventListParams as EventListParams
24
25
  from .state_list_params import StateListParams as StateListParams
agentex/types/agent.py CHANGED
@@ -1,6 +1,7 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  from typing import Optional
4
+ from datetime import datetime
4
5
  from typing_extensions import Literal
5
6
 
6
7
  from .._models import BaseModel
@@ -16,12 +17,18 @@ class Agent(BaseModel):
16
17
  acp_type: AcpType
17
18
  """The type of the ACP Server (Either sync or agentic)"""
18
19
 
20
+ created_at: datetime
21
+ """The timestamp when the agent was created"""
22
+
19
23
  description: str
20
24
  """The description of the action."""
21
25
 
22
26
  name: str
23
27
  """The unique name of the agent."""
24
28
 
29
+ updated_at: datetime
30
+ """The timestamp when the agent was last updated"""
31
+
25
32
  status: Optional[Literal["Pending", "Building", "Ready", "Failed", "Unknown"]] = None
26
33
  """The status of the action, indicating if it's building, ready, failed, etc."""
27
34
 
@@ -0,0 +1,14 @@
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+ from typing_extensions import TypedDict
7
+
8
+ __all__ = ["TaskListParams"]
9
+
10
+
11
+ class TaskListParams(TypedDict, total=False):
12
+ agent_id: Optional[str]
13
+
14
+ agent_name: Optional[str]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: agentex-sdk
3
- Version: 0.2.5
3
+ Version: 0.2.7
4
4
  Summary: The official Python library for the agentex API
5
5
  Project-URL: Homepage, https://github.com/scaleapi/agentex-python
6
6
  Project-URL: Repository, https://github.com/scaleapi/agentex-python
@@ -126,6 +126,37 @@ asyncio.run(main())
126
126
 
127
127
  Functionality between the synchronous and asynchronous clients is otherwise identical.
128
128
 
129
+ ## Debugging
130
+
131
+ AgentEx provides built-in debugging support for **temporal projects** during local development.
132
+
133
+ ```bash
134
+ # Basic debugging
135
+ uv run agentex agents run --manifest manifest.yaml --debug-worker
136
+
137
+ # Wait for debugger to attach before starting
138
+ uv run agentex agents run --manifest manifest.yaml --debug-worker --wait-for-debugger
139
+
140
+ # Custom debug port
141
+ uv run agentex agents run --manifest manifest.yaml --debug-worker --debug-port 5679
142
+ ```
143
+
144
+ For **VS Code**, add this configuration to `.vscode/launch.json`:
145
+
146
+ ```json
147
+ {
148
+ "name": "Attach to AgentEx Worker",
149
+ "type": "debugpy",
150
+ "request": "attach",
151
+ "connect": { "host": "localhost", "port": 5678 },
152
+ "pathMappings": [{ "localRoot": "${workspaceFolder}", "remoteRoot": "." }],
153
+ "justMyCode": false,
154
+ "console": "integratedTerminal"
155
+ }
156
+ ```
157
+
158
+ The debug server automatically finds an available port starting from 5678 and prints connection details when starting.
159
+
129
160
  ### With aiohttp
130
161
 
131
162
  By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.