indent 0.1.3__py3-none-any.whl → 0.1.4__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.
exponent/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.3" # Keep in sync with pyproject.toml
1
+ __version__ = "0.1.4" # Keep in sync with pyproject.toml
exponent/cli.py CHANGED
@@ -10,6 +10,7 @@ from exponent.commands.run_commands import run_cli
10
10
  from exponent.commands.shell_commands import shell_cli
11
11
  from exponent.commands.types import ExponentGroup, exponent_cli_group
12
12
  from exponent.commands.upgrade import upgrade_cli
13
+ from exponent.commands.workflow_commands import workflow_cli
13
14
  from exponent.utils.version import (
14
15
  get_installed_version,
15
16
  )
@@ -35,6 +36,7 @@ sources: list[ExponentGroup] = [
35
36
  cloud_cli, # Cloud commands
36
37
  listen_cli, # Listen to chat events
37
38
  github_app_cli, # Setup github app installation
39
+ workflow_cli, # Workflow commands
38
40
  ]
39
41
 
40
42
  for source in sources:
@@ -0,0 +1,111 @@
1
+ import asyncio
2
+ import os
3
+ import sys
4
+ from typing import cast
5
+
6
+ import click
7
+
8
+ from exponent.commands.run_commands import run_chat
9
+ from exponent.commands.settings import use_settings
10
+ from exponent.commands.types import exponent_cli_group
11
+ from exponent.core.config import Settings
12
+ from exponent.core.remote_execution.client import RemoteExecutionClient, WSDisconnected
13
+ from exponent.core.remote_execution.types import (
14
+ PrReviewWorkflowInput,
15
+ WorkflowTriggerResponse,
16
+ )
17
+
18
+
19
+ @exponent_cli_group(name="workflow")
20
+ def workflow_cli() -> None:
21
+ """Workflow commands."""
22
+ pass
23
+
24
+
25
+ @workflow_cli.group()
26
+ def workflow() -> None:
27
+ """Workflow management commands."""
28
+ pass
29
+
30
+
31
+ @workflow.command()
32
+ @use_settings
33
+ @click.argument("workflow_type", type=click.STRING)
34
+ def trigger(settings: Settings, workflow_type: str) -> None:
35
+ """Trigger a workflow."""
36
+
37
+ if not settings.api_key:
38
+ raise click.ClickException(
39
+ "No API key found. Use `indent login` to set your API key."
40
+ )
41
+
42
+ if workflow_type != "pr_review":
43
+ raise click.UsageError("Invalid workflow name. Only 'pr_review' is supported.")
44
+
45
+ loop = asyncio.get_event_loop()
46
+ response = loop.run_until_complete(trigger_pr_review_workflow(settings))
47
+
48
+ while True:
49
+ result = run_chat(
50
+ loop, settings.api_key, response.chat_uuid, settings, None, None
51
+ )
52
+ if result is None or isinstance(result, WSDisconnected):
53
+ # NOTE: None here means that handle_connection_changes exited
54
+ # first. We should likely have a different message for this.
55
+ if result and result.error_message:
56
+ click.secho(f"Error: {result.error_message}", fg="red")
57
+ sys.exit(10)
58
+ else:
59
+ print("Disconnected upon user request, shutting down...")
60
+ break
61
+ else:
62
+ raise click.ClickException("Workflow run exited unexpectedly")
63
+
64
+
65
+ async def _subprocess_check_output(command: str) -> str:
66
+ process = await asyncio.create_subprocess_shell(
67
+ command,
68
+ stdout=asyncio.subprocess.PIPE,
69
+ stderr=asyncio.subprocess.STDOUT,
70
+ )
71
+ stdout, _ = await process.communicate()
72
+
73
+ if process.returncode != 0:
74
+ output = stdout.decode().strip()
75
+ raise click.ClickException(
76
+ f"Command '{command}' failed with exit code {process.returncode}:\n{output}"
77
+ )
78
+
79
+ return stdout.decode().strip()
80
+
81
+
82
+ async def trigger_pr_review_workflow(settings: Settings) -> WorkflowTriggerResponse:
83
+ origin_url = await _subprocess_check_output("git ls-remote --get-url origin")
84
+ url = origin_url.strip().removesuffix(".git")
85
+ remote = url.split(":")[-1]
86
+ owner, repo = remote.split("/")[-2:]
87
+
88
+ pr_number_str = os.environ.get("PR_NUMBER")
89
+ if not pr_number_str:
90
+ raise click.ClickException("PR_NUMBER environment variable is not set")
91
+ try:
92
+ pr_number = int(pr_number_str)
93
+ except ValueError:
94
+ raise click.ClickException(
95
+ "PR_NUMBER environment variable is not a valid integer"
96
+ )
97
+
98
+ async with RemoteExecutionClient.session(
99
+ api_key=cast(str, settings.api_key),
100
+ base_url=settings.get_base_api_url(),
101
+ base_ws_url=settings.get_base_ws_url(),
102
+ working_directory=os.getcwd(),
103
+ ) as client:
104
+ return await client.trigger_workflow(
105
+ workflow_name="pr_review",
106
+ workflow_input=PrReviewWorkflowInput(
107
+ repo_owner=owner,
108
+ repo_name=repo,
109
+ pr_number=pr_number,
110
+ ),
111
+ )
@@ -54,9 +54,12 @@ from exponent.core.remote_execution.types import (
54
54
  CreateChatResponse,
55
55
  GitInfo,
56
56
  HeartbeatInfo,
57
+ PrReviewWorkflowInput,
57
58
  RemoteExecutionResponseType,
58
59
  RunWorkflowRequest,
59
60
  StreamingCodeExecutionRequest,
61
+ WorkflowTriggerRequest,
62
+ WorkflowTriggerResponse,
60
63
  )
61
64
  from exponent.core.remote_execution.utils import (
62
65
  deserialize_api_response,
@@ -378,6 +381,7 @@ class RemoteExecutionClient:
378
381
  )
379
382
  return cast(dict[str, Any], response.json())
380
383
 
384
+ # deprecated
381
385
  async def run_workflow(self, chat_uuid: str, workflow_id: str) -> dict[str, Any]:
382
386
  response = await self.api_client.post(
383
387
  "/api/remote_execution/run_workflow",
@@ -393,6 +397,18 @@ class RemoteExecutionClient:
393
397
  )
394
398
  return cast(dict[str, Any], response.json())
395
399
 
400
+ async def trigger_workflow(
401
+ self, workflow_name: str, workflow_input: PrReviewWorkflowInput
402
+ ) -> WorkflowTriggerResponse:
403
+ response = await self.api_client.post(
404
+ "/api/remote_execution/trigger_workflow",
405
+ json=WorkflowTriggerRequest(
406
+ workflow_name=workflow_name,
407
+ workflow_input=workflow_input,
408
+ ).model_dump(),
409
+ )
410
+ return await deserialize_api_response(response, WorkflowTriggerResponse)
411
+
396
412
  async def get_heartbeat_info(self) -> HeartbeatInfo:
397
413
  return HeartbeatInfo(
398
414
  system_info=await system_context.get_system_info(self.working_directory),
@@ -42,6 +42,23 @@ class RunWorkflowRequest(BaseModel):
42
42
  workflow_id: str
43
43
 
44
44
 
45
+ # note: before adding fields here, probably update
46
+ # get_workflow_run_by_trigger db query
47
+ class PrReviewWorkflowInput(BaseModel):
48
+ repo_owner: str
49
+ repo_name: str
50
+ pr_number: int
51
+
52
+
53
+ class WorkflowTriggerRequest(BaseModel):
54
+ workflow_name: str
55
+ workflow_input: PrReviewWorkflowInput
56
+
57
+
58
+ class WorkflowTriggerResponse(BaseModel):
59
+ chat_uuid: str
60
+
61
+
45
62
  class ExecutionEndResponse(BaseModel):
46
63
  execution_ended: bool
47
64
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: indent
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Indent is an AI Pair Programmer
5
5
  Author-email: Sashank Thupukari <sashank@exponent.run>
6
6
  Requires-Python: <3.13,>=3.10
@@ -1,5 +1,5 @@
1
- exponent/__init__.py,sha256=lpsrWC4wsfQ4-KiEMYUTzSNJlmYYtn6vNyEFjs6ddoc,58
2
- exponent/cli.py,sha256=9FPB5bqqGn7qVA_q2XZL6B2kRngVQoPdyvUvC5BH1_0,3496
1
+ exponent/__init__.py,sha256=EbzjWm450G-BHYY2B1fRN9gjMU0txzZ-JYSHtaTQGMs,58
2
+ exponent/cli.py,sha256=GA-Bw1nys9JNrlob0t3LcS9OZ4U5JcNYjR_r3kDD_qs,3596
3
3
  exponent/py.typed,sha256=9XZl5avs8yHp89XP_1Fjtbeg_2rjYorCC9I0k_j-h2c,334
4
4
  exponent/commands/cloud_commands.py,sha256=4DgS7PjCtCFB5uNN-szzAzOj16UU1D9b9_qS7DskoLE,2026
5
5
  exponent/commands/common.py,sha256=HUzc9yl5L6GZEF5DDsF3IeArwPKrghxmK43vxtkxgGs,13679
@@ -13,6 +13,7 @@ exponent/commands/theme.py,sha256=XoX9lYDf8YyoU1HoRdctDHjK5MrMRUcBALBLcHDVPc8,81
13
13
  exponent/commands/types.py,sha256=iDJL3hdwhO1PrhsJTJBioNYSKo0CWV8Nv-ONcDaWIRs,3670
14
14
  exponent/commands/upgrade.py,sha256=SpCdgEsBesPmXB5XgRJsIqonj73dfuuGVblo_0C2pWE,809
15
15
  exponent/commands/utils.py,sha256=gmWUPHZtAfuSb3zdrAEkmHVhQYmHghqhvJUFBunn2-4,7572
16
+ exponent/commands/workflow_commands.py,sha256=ytetNMzojzJDe9NAsGdkDBGJs1eZIfY8xLNqesnc3AE,3580
16
17
  exponent/core/config.py,sha256=TNFLUgLnfSocRMVSav_7E4VcaNHXZ_3Mg5Lp1smP46U,5731
17
18
  exponent/core/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
19
  exponent/core/graphql/client.py,sha256=KRveVt4lsrSdF9PnhhlQeqx74FQ9-iN28WQp0WKnQ30,1921
@@ -24,7 +25,7 @@ exponent/core/graphql/queries.py,sha256=TXXHLGb7QpeICaofowVYsjyHDfqjCoQ3omLbesuw
24
25
  exponent/core/graphql/subscriptions.py,sha256=gg42wG5HqEuNMJU7OUHruNCAGtM6FKPLRD7KfjcKjC4,9995
25
26
  exponent/core/remote_execution/checkpoints.py,sha256=3QGYMLa8vT7XmxMYTRcGrW8kNGHwRC0AkUfULribJWg,6354
26
27
  exponent/core/remote_execution/cli_rpc_types.py,sha256=3Yj6kIkqPAf7FNNni5V4XNe8SLPoTgbcL6swesW7cw4,4593
27
- exponent/core/remote_execution/client.py,sha256=3QbSAsukuVHCwmGfkoZBaBwgy4DuRZQs7LGAJtmT068,21010
28
+ exponent/core/remote_execution/client.py,sha256=u5JH1yhbHcWpdee340x9JnYCsFJ-rJuu0-YPgSbojRw,21603
28
29
  exponent/core/remote_execution/code_execution.py,sha256=jYPB_7dJzS9BTPLX9fKQpsFPatwjbXuaFFSxT9tDTfI,2388
29
30
  exponent/core/remote_execution/error_info.py,sha256=Rd7OA3ps06qYejPVcOaMBB9AtftP3wqQoOfiILFASnc,1378
30
31
  exponent/core/remote_execution/exceptions.py,sha256=eT57lBnBhvh-KJ5lsKWcfgGA5-WisAxhjZx-Z6OupZY,135
@@ -35,7 +36,7 @@ exponent/core/remote_execution/session.py,sha256=cSJcCG1o74mBE6lZS_9VFmhyZdW6BeI
35
36
  exponent/core/remote_execution/system_context.py,sha256=0FkbsSxEVjdjTF0tQpOkYK_VaVM126C3_K8QP0YXxOs,1510
36
37
  exponent/core/remote_execution/tool_execution.py,sha256=G7wB_DA0jANqywhm_a6lbWtwjCcpQsuQHlExGohsgeY,9233
37
38
  exponent/core/remote_execution/truncation.py,sha256=rFQDfT7qf_u6lvhEADWSpoRe_GTPegXXqknk7OZp0uI,10093
38
- exponent/core/remote_execution/types.py,sha256=G-5EU-NtIj_zZbV305YMDZEcSacGK7QfzEdCYSHAGmA,17206
39
+ exponent/core/remote_execution/types.py,sha256=3x73SJ3jQtaIu01musXgS-VF_MYwYucG3hyA2nWHbII,17567
39
40
  exponent/core/remote_execution/utils.py,sha256=Hw2zGMq0NKVwkVzNOVekAvpM33rfIz2P36mEcJr43Ag,20688
40
41
  exponent/core/remote_execution/languages/python_execution.py,sha256=GcQU5PiAOUD9tUZDrSObgM3L7FjXMdzr4FQg6ts2XjY,7780
41
42
  exponent/core/remote_execution/languages/shell_streaming.py,sha256=eSeHcK_unGpVRRdfgfwhml_tSlCo0VCeUEQY4zuMbDw,7372
@@ -49,7 +50,7 @@ exponent/migration-docs/login.md,sha256=KIeXy3m2nzSUgw-4PW1XzXfHael1D4Zu93CplLMb
49
50
  exponent/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
51
  exponent/utils/colors.py,sha256=HBkqe_ZmhJ9YiL2Fpulqek4KvLS5mwBTY4LQSM5N8SM,2762
51
52
  exponent/utils/version.py,sha256=Q4txP7Rg_KO0u0tUpx8O0DoOt32wrX7ctNeDXVKaOfA,8835
52
- indent-0.1.3.dist-info/METADATA,sha256=IMTWgeClAXkVN9EFndRFeNFkx77eQQ1PI7_nhVCgjXU,1283
53
- indent-0.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
54
- indent-0.1.3.dist-info/entry_points.txt,sha256=q8q1t1sbl4NULGOR0OV5RmSG4KEjkpEQRU_RUXEGzcs,44
55
- indent-0.1.3.dist-info/RECORD,,
53
+ indent-0.1.4.dist-info/METADATA,sha256=OTkZrr6729d5hBErMwl_RqvSr1ZrLRfCsx-MiuWA2b8,1283
54
+ indent-0.1.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
55
+ indent-0.1.4.dist-info/entry_points.txt,sha256=q8q1t1sbl4NULGOR0OV5RmSG4KEjkpEQRU_RUXEGzcs,44
56
+ indent-0.1.4.dist-info/RECORD,,
File without changes