coala-cli 0.2.4__tar.gz → 0.2.6__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.
@@ -0,0 +1,6 @@
1
+ __pycache__/
2
+ dist/
3
+ docs
4
+ Pipfile.lock
5
+ *archieve*
6
+ .pypirc
@@ -1,21 +1,21 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: coala-cli
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: Command-line LLM-agent Adapter
5
- License: MIT
6
5
  Author: Qiang
7
- Requires-Python: >=3.12,<3.15
6
+ License-Expression: MIT
8
7
  Classifier: License :: OSI Approved :: MIT License
9
8
  Classifier: Programming Language :: Python :: 3
10
9
  Classifier: Programming Language :: Python :: 3.12
11
10
  Classifier: Programming Language :: Python :: 3.13
12
11
  Classifier: Programming Language :: Python :: 3.14
13
- Requires-Dist: cwltool (>=3.2.20260413085819,<4.0.0)
14
- Requires-Dist: fastapi (>=0.115.0)
15
- Requires-Dist: mcp (>=1.12.0,<2.0.0)
16
- Requires-Dist: pydantic (>=2.10.0,<3.0.0)
17
- Requires-Dist: requests (>=2.33.0,<3.0.0)
18
- Requires-Dist: uvicorn (>=0.32.0,<0.33.0)
12
+ Requires-Python: <3.15,>=3.12
13
+ Requires-Dist: cwltool<4.0.0,>=3.2.20260413085819
14
+ Requires-Dist: fastapi>=0.115.0
15
+ Requires-Dist: mcp<3,>=2.2
16
+ Requires-Dist: pydantic<3.0.0,>=2.10.0
17
+ Requires-Dist: requests<3.0.0,>=2.33.0
18
+ Requires-Dist: uvicorn<0.33.0,>=0.32.0
19
19
  Description-Content-Type: text/markdown
20
20
 
21
21
  # Coala: Command-line LLM-agent Adapter
@@ -29,4 +29,3 @@ Coala integrates the [Common Workflow Language (CWL)](https://www.commonwl.org/s
29
29
 
30
30
  Please find Coala documuentations through [Coala website](https://coala.info/), including [overview](https://coala.info/guide/), [installation](https://coala.info/guide/installation.html), [quick start](https://coala.info/guide/quick-start.html), and [use cases](https://coala.info/use-cases/).
31
31
 
32
-
@@ -15,7 +15,7 @@ from cwltool.context import RuntimeContext
15
15
  from threading import Thread
16
16
  import time
17
17
  from typing import Optional, List, Annotated
18
- from mcp.server.fastmcp import FastMCP
18
+ from mcp.server.mcpserver import MCPServer
19
19
  from coala.tool_logic import run_tool, configure_container_runner # <-- import shared logic
20
20
  import threading
21
21
  import sys
@@ -57,7 +57,8 @@ class mcp_api():
57
57
  self.container_runner = container_runner
58
58
  self.server = None
59
59
  self.url = None
60
- self.mcp = FastMCP(host=host, port=port)
60
+ # mcp 2: host and port are run() options, no longer constructor arguments.
61
+ self.mcp = MCPServer()
61
62
  self.tools = {} # tool_name -> tool info
62
63
  self.system_prompt = """
63
64
  At the end of your response, append a summary listing the tool name and version from the tool's description using this exact format:
@@ -521,7 +522,10 @@ tool_version: <TOOL_VERSION>
521
522
  # For stdio transport, don't print startup messages to stdout
522
523
  logger.info("Starting MCP server with stdio transport")
523
524
 
524
- self.mcp.run(transport=transport)
525
+ if transport == 'streamable-http':
526
+ self.mcp.run(transport=transport, host=self.host, port=self.port)
527
+ else:
528
+ self.mcp.run(transport=transport)
525
529
  # thread = threading.Thread(target=self.mcp.run, kwargs={'transport': 'sse'}, daemon=True)
526
530
  # thread.start()
527
531
  # self.server_thread = thread
@@ -6,6 +6,7 @@ from pathlib import Path
6
6
  from urllib.parse import urlparse, unquote
7
7
  from urllib.request import url2pathname
8
8
  from cwltool.context import RuntimeContext
9
+ from cwltool.factory import WorkflowStatus
9
10
 
10
11
 
11
12
  def _canonical_file_uri(uri: str) -> str:
@@ -57,6 +58,66 @@ def configure_container_runner(runtime_context: RuntimeContext, container_runner
57
58
  or shutil.which('xcrunner') or 'xcrunner'
58
59
  )
59
60
 
61
+ class ToolRunFailed(RuntimeError):
62
+ """A CWL run that exited non-zero, carrying what the tool itself printed."""
63
+
64
+
65
+ # Enough to hold a tool's closing error and the lines that lead to it, without flooding the
66
+ # caller with a long progress log.
67
+ _FAILED_OUTPUT_LINES = 40
68
+ _FAILED_OUTPUT_CHARS = 6000
69
+
70
+
71
+ def _captured_stream_outputs(tool) -> dict:
72
+ """``{output id: "stdout" | "stderr"}`` for outputs that capture the tool's own streams.
73
+
74
+ cwltool rewrites ``type: stdout`` into a ``File`` whose glob is the tool's ``stdout`` value,
75
+ so the glob, not the type, identifies it — and still does when that value is an expression.
76
+ """
77
+ spec = getattr(getattr(tool, "t", None), "tool", None) or {}
78
+ streams = {spec.get(s): s for s in ("stdout", "stderr") if spec.get(s)}
79
+ found = {}
80
+ for out in spec.get("outputs") or []:
81
+ glob = (out.get("outputBinding") or {}).get("glob")
82
+ if isinstance(glob, str) and glob in streams:
83
+ found[str(out.get("id", "")).rsplit("#", 1)[-1]] = streams[glob]
84
+ return found
85
+
86
+
87
+ def _tail(path: str) -> str:
88
+ try:
89
+ with open(path, "r", encoding="utf-8", errors="replace") as fh:
90
+ lines = fh.read().splitlines()
91
+ except OSError:
92
+ return ""
93
+ text = "\n".join(lines[-_FAILED_OUTPUT_LINES:]).strip()
94
+ return text[-_FAILED_OUTPUT_CHARS:]
95
+
96
+
97
+ def _failed_run_output(tool, partial) -> str:
98
+ """The tail of the tool's own stdout/stderr capture files from a failed run, or ``""``.
99
+
100
+ A tool that prints its error to standard output, into a file the CWL declares with
101
+ ``stdout:``, leaves cwltool's log with nothing but "exited with status: 1": cellranger-arc
102
+ ``mkref`` reported an unknown GTF contig that way, and the caller retried blind for three
103
+ hours. cwltool still collects that file when the run fails and passes it in the exception,
104
+ so read it back.
105
+ """
106
+ if not isinstance(partial, dict):
107
+ return ""
108
+ parts = []
109
+ for out_id, stream in _captured_stream_outputs(tool).items():
110
+ entry = partial.get(out_id)
111
+ location = entry.get("location") if isinstance(entry, dict) else None
112
+ if not isinstance(location, str) or not location.startswith("file://"):
113
+ continue
114
+ text = _tail(url2pathname(urlparse(location).path))
115
+ if text:
116
+ name = entry.get("basename") or out_id
117
+ parts.append(f"The tool's {stream} ({name}), last lines:\n{text}")
118
+ return "\n\n".join(parts)
119
+
120
+
60
121
  def _read_file_content(filepath):
61
122
  """Read file content, handling gzipped files."""
62
123
  try:
@@ -198,7 +259,13 @@ def run_tool(tool, params, outputs, read_outs=False, container_runner=None):
198
259
  configure_container_runner(original_runtime_context, container_runner)
199
260
 
200
261
  # Execute tool (no need to pass runtime_context if we modified it in place)
201
- res = tool(**params)
262
+ try:
263
+ res = tool(**params)
264
+ except WorkflowStatus as exc:
265
+ detail = _failed_run_output(tool, exc.out)
266
+ if not detail:
267
+ raise
268
+ raise ToolRunFailed(f"Completed {exc.status}.\n\n{detail}") from exc
202
269
  outs = {}
203
270
  for ot in outputs:
204
271
  out_content = res[ot['name']]
@@ -0,0 +1,42 @@
1
+ [project]
2
+ name = "coala-cli"
3
+ version = "0.2.6"
4
+ description = "Command-line LLM-agent Adapter"
5
+ authors = [{ name = "Qiang" }]
6
+ license = "MIT"
7
+ readme = "README.md"
8
+ requires-python = ">=3.12,<3.15"
9
+ classifiers = [
10
+ "License :: OSI Approved :: MIT License",
11
+ "Programming Language :: Python :: 3",
12
+ "Programming Language :: Python :: 3.12",
13
+ "Programming Language :: Python :: 3.13",
14
+ "Programming Language :: Python :: 3.14",
15
+ ]
16
+ dependencies = [
17
+ "fastapi>=0.115.0",
18
+ "requests>=2.33.0,<3.0.0",
19
+ "pydantic>=2.10.0,<3.0.0",
20
+ "uvicorn>=0.32.0,<0.33.0",
21
+ "cwltool>=3.2.20260413085819,<4.0.0",
22
+ "mcp>=2.2,<3",
23
+ ]
24
+
25
+ [dependency-groups]
26
+ dev = ["pytest>=8.0.0", "pytest-asyncio>=0.24.0"]
27
+
28
+ [build-system]
29
+ requires = ["hatchling"]
30
+ build-backend = "hatchling.build"
31
+
32
+ # The sdist carries what the Poetry build did: the package and the README.
33
+ [tool.hatch.build.targets.sdist]
34
+ include = ["coala", "README.md"]
35
+
36
+ [tool.hatch.build.targets.wheel]
37
+ packages = ["coala"]
38
+
39
+ [tool.pytest.ini_options]
40
+ filterwarnings = [
41
+ "ignore::pydantic.warnings.UnsupportedFieldAttributeWarning",
42
+ ]
@@ -1,27 +0,0 @@
1
- [tool.poetry]
2
- name = "coala-cli"
3
- version = "0.2.4"
4
- description = "Command-line LLM-agent Adapter"
5
- authors = ["Qiang"]
6
- license = "MIT"
7
- readme = "README.md"
8
- packages = [{ include = "coala" }]
9
-
10
- [tool.poetry.dependencies]
11
- python = ">=3.12,<3.15"
12
- fastapi = ">=0.115.0"
13
- requests = "^2.33.0"
14
- pydantic = "^2.10.0"
15
- uvicorn = "^0.32.0"
16
- cwltool = "^3.2.20260413085819"
17
- mcp = "^1.12.0"
18
-
19
-
20
- [build-system]
21
- requires = ["poetry-core"]
22
- build-backend = "poetry.core.masonry.api"
23
-
24
- [tool.pytest.ini_options]
25
- filterwarnings = [
26
- "ignore::pydantic.warnings.UnsupportedFieldAttributeWarning",
27
- ]
File without changes
File without changes
File without changes
File without changes