sysmlv2copilot 0.2.0__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.
- sysmlv2copilot/__init__.py +49 -0
- sysmlv2copilot/__main__.py +5 -0
- sysmlv2copilot/cli.py +121 -0
- sysmlv2copilot/client.py +197 -0
- sysmlv2copilot/exceptions.py +40 -0
- sysmlv2copilot/types.py +112 -0
- sysmlv2copilot-0.2.0.dist-info/METADATA +432 -0
- sysmlv2copilot-0.2.0.dist-info/RECORD +12 -0
- sysmlv2copilot-0.2.0.dist-info/WHEEL +5 -0
- sysmlv2copilot-0.2.0.dist-info/entry_points.txt +2 -0
- sysmlv2copilot-0.2.0.dist-info/licenses/LICENSE +21 -0
- sysmlv2copilot-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
2
|
+
|
|
3
|
+
from .client import SysMLCopilot
|
|
4
|
+
from .exceptions import (
|
|
5
|
+
APIConnectionError,
|
|
6
|
+
APIResponseValidationError,
|
|
7
|
+
APIStatusError,
|
|
8
|
+
AuthenticationError,
|
|
9
|
+
RateLimitError,
|
|
10
|
+
SysMLCopilotError,
|
|
11
|
+
)
|
|
12
|
+
from .types import (
|
|
13
|
+
ArtifactReference,
|
|
14
|
+
DependencyStatus,
|
|
15
|
+
ErrorPayload,
|
|
16
|
+
HealthResponse,
|
|
17
|
+
RepairCreateParams,
|
|
18
|
+
RepairObject,
|
|
19
|
+
RequestDetail,
|
|
20
|
+
ResponseCreateParams,
|
|
21
|
+
ResponseMetadata,
|
|
22
|
+
ResponseObject,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
__version__ = version("sysmlv2copilot")
|
|
27
|
+
except PackageNotFoundError:
|
|
28
|
+
__version__ = "0.2.0"
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"APIConnectionError",
|
|
32
|
+
"APIResponseValidationError",
|
|
33
|
+
"APIStatusError",
|
|
34
|
+
"ArtifactReference",
|
|
35
|
+
"AuthenticationError",
|
|
36
|
+
"DependencyStatus",
|
|
37
|
+
"ErrorPayload",
|
|
38
|
+
"HealthResponse",
|
|
39
|
+
"RateLimitError",
|
|
40
|
+
"RepairCreateParams",
|
|
41
|
+
"RepairObject",
|
|
42
|
+
"RequestDetail",
|
|
43
|
+
"ResponseCreateParams",
|
|
44
|
+
"ResponseMetadata",
|
|
45
|
+
"ResponseObject",
|
|
46
|
+
"SysMLCopilot",
|
|
47
|
+
"SysMLCopilotError",
|
|
48
|
+
"__version__",
|
|
49
|
+
]
|
sysmlv2copilot/cli.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from . import __version__
|
|
10
|
+
from .client import SysMLCopilot
|
|
11
|
+
from .exceptions import SysMLCopilotError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
15
|
+
parser = argparse.ArgumentParser(
|
|
16
|
+
prog="sysmlv2copilot",
|
|
17
|
+
description="CLI client for the SysML refinement API.",
|
|
18
|
+
)
|
|
19
|
+
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
20
|
+
parser.add_argument(
|
|
21
|
+
"--base-url",
|
|
22
|
+
default=os.getenv("SYSMLV2COPILOT_BASE_URL", "http://127.0.0.1:8000"),
|
|
23
|
+
help="API base URL.",
|
|
24
|
+
)
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
"--api-key",
|
|
27
|
+
default=os.getenv("SYSMLV2COPILOT_API_KEY"),
|
|
28
|
+
help="Bearer API key. Defaults to SYSMLV2COPILOT_API_KEY.",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
32
|
+
|
|
33
|
+
responses_parser = subparsers.add_parser("responses", help="Create a refinement response.")
|
|
34
|
+
responses_subparsers = responses_parser.add_subparsers(dest="responses_command", required=True)
|
|
35
|
+
create_parser = responses_subparsers.add_parser("create", help="Create a response.")
|
|
36
|
+
create_group = create_parser.add_mutually_exclusive_group(required=True)
|
|
37
|
+
create_group.add_argument("--input", help="Inline natural-language input text.")
|
|
38
|
+
create_group.add_argument("--input-file", type=Path, help="Path to a file containing the input.")
|
|
39
|
+
create_parser.add_argument("--model", default="refine-sysml-v1")
|
|
40
|
+
create_parser.add_argument("--provider", choices=["openai", "anthropic"])
|
|
41
|
+
create_parser.add_argument("--upstream-model")
|
|
42
|
+
create_parser.add_argument("--max-iters", type=int, default=10)
|
|
43
|
+
create_parser.add_argument("--max-total-tokens", type=int, default=40_000)
|
|
44
|
+
create_parser.add_argument("--temperature", type=float)
|
|
45
|
+
|
|
46
|
+
repairs_parser = subparsers.add_parser("repairs", help="Repair existing SysML.")
|
|
47
|
+
repairs_subparsers = repairs_parser.add_subparsers(dest="repairs_command", required=True)
|
|
48
|
+
repair_create_parser = repairs_subparsers.add_parser("create", help="Repair a SysML document.")
|
|
49
|
+
repair_group = repair_create_parser.add_mutually_exclusive_group(required=True)
|
|
50
|
+
repair_group.add_argument("--input", help="Inline SysML input text.")
|
|
51
|
+
repair_group.add_argument("--input-file", type=Path, help="Path to a file containing SysML.")
|
|
52
|
+
repair_create_parser.add_argument("--model", default="refine-sysml-v1")
|
|
53
|
+
repair_create_parser.add_argument("--provider", choices=["openai", "anthropic"])
|
|
54
|
+
repair_create_parser.add_argument("--upstream-model")
|
|
55
|
+
repair_create_parser.add_argument("--max-iters", type=int, default=10)
|
|
56
|
+
repair_create_parser.add_argument("--max-total-tokens", type=int, default=40_000)
|
|
57
|
+
repair_create_parser.add_argument("--temperature", type=float)
|
|
58
|
+
|
|
59
|
+
requests_parser = subparsers.add_parser("requests", help="Fetch a stored request.")
|
|
60
|
+
requests_subparsers = requests_parser.add_subparsers(dest="requests_command", required=True)
|
|
61
|
+
retrieve_parser = requests_subparsers.add_parser("retrieve", help="Retrieve a request by id.")
|
|
62
|
+
retrieve_parser.add_argument("request_id")
|
|
63
|
+
|
|
64
|
+
health_parser = subparsers.add_parser("health", help="Check service health.")
|
|
65
|
+
health_subparsers = health_parser.add_subparsers(dest="health_command", required=True)
|
|
66
|
+
health_subparsers.add_parser("check", help="Get /healthz.")
|
|
67
|
+
return parser
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _read_input_text(args: argparse.Namespace) -> str:
|
|
71
|
+
if args.input is not None:
|
|
72
|
+
return args.input
|
|
73
|
+
return args.input_file.read_text(encoding="utf-8")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _print_json(data: Any) -> None:
|
|
77
|
+
print(json.dumps(data, indent=2, sort_keys=True))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def main(argv: list[str] | None = None) -> int:
|
|
81
|
+
parser = build_parser()
|
|
82
|
+
args = parser.parse_args(argv)
|
|
83
|
+
try:
|
|
84
|
+
with SysMLCopilot(api_key=args.api_key, base_url=args.base_url) as client:
|
|
85
|
+
if args.command == "responses" and args.responses_command == "create":
|
|
86
|
+
response = client.responses.create(
|
|
87
|
+
input=_read_input_text(args),
|
|
88
|
+
model=args.model,
|
|
89
|
+
provider=args.provider,
|
|
90
|
+
upstream_model=args.upstream_model,
|
|
91
|
+
max_iters=args.max_iters,
|
|
92
|
+
max_total_tokens=args.max_total_tokens,
|
|
93
|
+
temperature=args.temperature,
|
|
94
|
+
)
|
|
95
|
+
_print_json(response.model_dump())
|
|
96
|
+
return 0
|
|
97
|
+
if args.command == "repairs" and args.repairs_command == "create":
|
|
98
|
+
response = client.repairs.create(
|
|
99
|
+
input=_read_input_text(args),
|
|
100
|
+
model=args.model,
|
|
101
|
+
provider=args.provider,
|
|
102
|
+
upstream_model=args.upstream_model,
|
|
103
|
+
max_iters=args.max_iters,
|
|
104
|
+
max_total_tokens=args.max_total_tokens,
|
|
105
|
+
temperature=args.temperature,
|
|
106
|
+
)
|
|
107
|
+
_print_json(response.model_dump())
|
|
108
|
+
return 0
|
|
109
|
+
if args.command == "requests" and args.requests_command == "retrieve":
|
|
110
|
+
detail = client.requests.retrieve(args.request_id)
|
|
111
|
+
_print_json(detail.model_dump())
|
|
112
|
+
return 0
|
|
113
|
+
if args.command == "health" and args.health_command == "check":
|
|
114
|
+
health = client.health.check()
|
|
115
|
+
_print_json(health.model_dump())
|
|
116
|
+
return 0
|
|
117
|
+
except SysMLCopilotError as exc:
|
|
118
|
+
print(f"error: {exc}")
|
|
119
|
+
return 1
|
|
120
|
+
parser.error("Unsupported command.")
|
|
121
|
+
return 2
|
sysmlv2copilot/client.py
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
from pydantic import ValidationError
|
|
8
|
+
|
|
9
|
+
from .exceptions import (
|
|
10
|
+
APIConnectionError,
|
|
11
|
+
APIResponseValidationError,
|
|
12
|
+
APIStatusError,
|
|
13
|
+
AuthenticationError,
|
|
14
|
+
RateLimitError,
|
|
15
|
+
)
|
|
16
|
+
from .types import (
|
|
17
|
+
HealthResponse,
|
|
18
|
+
RepairCreateParams,
|
|
19
|
+
RepairObject,
|
|
20
|
+
RequestDetail,
|
|
21
|
+
ResponseCreateParams,
|
|
22
|
+
ResponseObject,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _normalize_base_url(base_url: str) -> str:
|
|
27
|
+
return base_url.rstrip("/")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class _BaseResource:
|
|
31
|
+
def __init__(self, client: "SysMLCopilot") -> None:
|
|
32
|
+
self._client = client
|
|
33
|
+
|
|
34
|
+
def _request(self, method: str, path: str, *, json_body: Mapping[str, Any] | None = None) -> Any:
|
|
35
|
+
return self._client._request(method, path, json_body=json_body)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ResponsesResource(_BaseResource):
|
|
39
|
+
def create(
|
|
40
|
+
self,
|
|
41
|
+
*,
|
|
42
|
+
input: str,
|
|
43
|
+
model: str = "refine-sysml-v1",
|
|
44
|
+
provider: str | None = None,
|
|
45
|
+
upstream_model: str | None = None,
|
|
46
|
+
max_iters: int = 10,
|
|
47
|
+
max_total_tokens: int = 40_000,
|
|
48
|
+
temperature: float | None = None,
|
|
49
|
+
) -> ResponseObject:
|
|
50
|
+
payload = ResponseCreateParams(
|
|
51
|
+
input=input,
|
|
52
|
+
model=model,
|
|
53
|
+
provider=provider,
|
|
54
|
+
upstream_model=upstream_model,
|
|
55
|
+
max_iters=max_iters,
|
|
56
|
+
max_total_tokens=max_total_tokens,
|
|
57
|
+
temperature=temperature,
|
|
58
|
+
)
|
|
59
|
+
data = self._request(
|
|
60
|
+
"POST",
|
|
61
|
+
"/v1/responses",
|
|
62
|
+
json_body=payload.model_dump(exclude_none=True),
|
|
63
|
+
)
|
|
64
|
+
try:
|
|
65
|
+
return ResponseObject.model_validate(data)
|
|
66
|
+
except ValidationError as exc:
|
|
67
|
+
raise APIResponseValidationError(f"Invalid response payload: {exc}") from exc
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class RequestsResource(_BaseResource):
|
|
71
|
+
def retrieve(self, request_id: str) -> RequestDetail:
|
|
72
|
+
data = self._request("GET", f"/v1/requests/{request_id}")
|
|
73
|
+
try:
|
|
74
|
+
return RequestDetail.model_validate(data)
|
|
75
|
+
except ValidationError as exc:
|
|
76
|
+
raise APIResponseValidationError(f"Invalid request detail payload: {exc}") from exc
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class RepairsResource(_BaseResource):
|
|
80
|
+
def create(
|
|
81
|
+
self,
|
|
82
|
+
*,
|
|
83
|
+
input: str,
|
|
84
|
+
model: str = "refine-sysml-v1",
|
|
85
|
+
provider: str | None = None,
|
|
86
|
+
upstream_model: str | None = None,
|
|
87
|
+
max_iters: int = 10,
|
|
88
|
+
max_total_tokens: int = 40_000,
|
|
89
|
+
temperature: float | None = None,
|
|
90
|
+
) -> RepairObject:
|
|
91
|
+
payload = RepairCreateParams(
|
|
92
|
+
input=input,
|
|
93
|
+
model=model,
|
|
94
|
+
provider=provider,
|
|
95
|
+
upstream_model=upstream_model,
|
|
96
|
+
max_iters=max_iters,
|
|
97
|
+
max_total_tokens=max_total_tokens,
|
|
98
|
+
temperature=temperature,
|
|
99
|
+
)
|
|
100
|
+
data = self._request(
|
|
101
|
+
"POST",
|
|
102
|
+
"/v1/repairs",
|
|
103
|
+
json_body=payload.model_dump(exclude_none=True),
|
|
104
|
+
)
|
|
105
|
+
try:
|
|
106
|
+
return RepairObject.model_validate(data)
|
|
107
|
+
except ValidationError as exc:
|
|
108
|
+
raise APIResponseValidationError(f"Invalid repair payload: {exc}") from exc
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class HealthResource(_BaseResource):
|
|
112
|
+
def check(self) -> HealthResponse:
|
|
113
|
+
data = self._request("GET", "/healthz")
|
|
114
|
+
try:
|
|
115
|
+
return HealthResponse.model_validate(data)
|
|
116
|
+
except ValidationError as exc:
|
|
117
|
+
raise APIResponseValidationError(f"Invalid health payload: {exc}") from exc
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class SysMLCopilot:
|
|
121
|
+
def __init__(
|
|
122
|
+
self,
|
|
123
|
+
*,
|
|
124
|
+
api_key: str | None = None,
|
|
125
|
+
base_url: str = "http://127.0.0.1:8000",
|
|
126
|
+
timeout: float = 120.0,
|
|
127
|
+
http_client: httpx.Client | None = None,
|
|
128
|
+
default_headers: Mapping[str, str] | None = None,
|
|
129
|
+
) -> None:
|
|
130
|
+
self.base_url = _normalize_base_url(base_url)
|
|
131
|
+
self._owns_client = http_client is None
|
|
132
|
+
headers = {"Accept": "application/json"}
|
|
133
|
+
if default_headers:
|
|
134
|
+
headers.update(default_headers)
|
|
135
|
+
if api_key:
|
|
136
|
+
headers["Authorization"] = f"Bearer {api_key}"
|
|
137
|
+
if http_client is None:
|
|
138
|
+
self._client = httpx.Client(
|
|
139
|
+
base_url=self.base_url,
|
|
140
|
+
headers=headers,
|
|
141
|
+
timeout=timeout,
|
|
142
|
+
)
|
|
143
|
+
else:
|
|
144
|
+
http_client.headers.update(headers)
|
|
145
|
+
self._client = http_client
|
|
146
|
+
self.responses = ResponsesResource(self)
|
|
147
|
+
self.repairs = RepairsResource(self)
|
|
148
|
+
self.requests = RequestsResource(self)
|
|
149
|
+
self.health = HealthResource(self)
|
|
150
|
+
|
|
151
|
+
def close(self) -> None:
|
|
152
|
+
if self._owns_client:
|
|
153
|
+
self._client.close()
|
|
154
|
+
|
|
155
|
+
def __enter__(self) -> "SysMLCopilot":
|
|
156
|
+
return self
|
|
157
|
+
|
|
158
|
+
def __exit__(self, exc_type, exc, tb) -> None:
|
|
159
|
+
self.close()
|
|
160
|
+
|
|
161
|
+
def _request(self, method: str, path: str, *, json_body: Mapping[str, Any] | None = None) -> Any:
|
|
162
|
+
try:
|
|
163
|
+
response = self._client.request(method, path, json=json_body)
|
|
164
|
+
except httpx.HTTPError as exc:
|
|
165
|
+
raise APIConnectionError(f"Request failed: {exc}") from exc
|
|
166
|
+
if response.is_error:
|
|
167
|
+
self._raise_for_status(response)
|
|
168
|
+
try:
|
|
169
|
+
return response.json()
|
|
170
|
+
except ValueError as exc:
|
|
171
|
+
raise APIResponseValidationError(f"Response was not valid JSON: {exc}") from exc
|
|
172
|
+
|
|
173
|
+
def _raise_for_status(self, response: httpx.Response) -> None:
|
|
174
|
+
body: Any
|
|
175
|
+
try:
|
|
176
|
+
body = response.json()
|
|
177
|
+
except ValueError:
|
|
178
|
+
body = response.text
|
|
179
|
+
|
|
180
|
+
detail = body.get("detail") if isinstance(body, dict) else None
|
|
181
|
+
payload = detail.get("error") if isinstance(detail, dict) else None
|
|
182
|
+
error_type = payload.get("type") if isinstance(payload, dict) else None
|
|
183
|
+
message = payload.get("message") if isinstance(payload, dict) else response.text
|
|
184
|
+
|
|
185
|
+
exc_cls: type[APIStatusError]
|
|
186
|
+
if response.status_code == 401:
|
|
187
|
+
exc_cls = AuthenticationError
|
|
188
|
+
elif response.status_code == 429:
|
|
189
|
+
exc_cls = RateLimitError
|
|
190
|
+
else:
|
|
191
|
+
exc_cls = APIStatusError
|
|
192
|
+
raise exc_cls(
|
|
193
|
+
message or f"API request failed with status {response.status_code}",
|
|
194
|
+
status_code=response.status_code,
|
|
195
|
+
body=body,
|
|
196
|
+
error_type=error_type,
|
|
197
|
+
)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SysMLCopilotError(Exception):
|
|
7
|
+
"""Base SDK exception."""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class APIConnectionError(SysMLCopilotError):
|
|
11
|
+
"""Raised when the API cannot be reached."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class APIResponseValidationError(SysMLCopilotError):
|
|
15
|
+
"""Raised when the API response shape is invalid."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class APIStatusError(SysMLCopilotError):
|
|
19
|
+
"""Raised for non-2xx API responses."""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
message: str,
|
|
24
|
+
*,
|
|
25
|
+
status_code: int,
|
|
26
|
+
body: Any | None = None,
|
|
27
|
+
error_type: str | None = None,
|
|
28
|
+
) -> None:
|
|
29
|
+
super().__init__(message)
|
|
30
|
+
self.status_code = status_code
|
|
31
|
+
self.body = body
|
|
32
|
+
self.error_type = error_type
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class AuthenticationError(APIStatusError):
|
|
36
|
+
"""Raised for 401 responses."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class RateLimitError(APIStatusError):
|
|
40
|
+
"""Raised for 429 responses."""
|
sysmlv2copilot/types.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ResponseCreateParams(BaseModel):
|
|
9
|
+
input: str
|
|
10
|
+
model: str = "refine-sysml-v1"
|
|
11
|
+
provider: Literal["openai", "anthropic"] | None = None
|
|
12
|
+
upstream_model: str | None = None
|
|
13
|
+
max_iters: int = Field(default=10, ge=1, le=50)
|
|
14
|
+
max_total_tokens: int = Field(default=40_000, ge=1, le=250_000)
|
|
15
|
+
temperature: float | None = Field(default=None, ge=0, le=2)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class RepairCreateParams(ResponseCreateParams):
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ErrorPayload(BaseModel):
|
|
23
|
+
type: str
|
|
24
|
+
message: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ArtifactReference(BaseModel):
|
|
28
|
+
id: str
|
|
29
|
+
artifact_type: str
|
|
30
|
+
storage_backend: str
|
|
31
|
+
storage_path: str
|
|
32
|
+
content_type: str
|
|
33
|
+
size_bytes: int
|
|
34
|
+
created_at: str
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ResponseMetadata(BaseModel):
|
|
38
|
+
operation: Literal["generate", "repair"] | None = None
|
|
39
|
+
provider: Literal["openai", "anthropic"] | None = None
|
|
40
|
+
upstream_model: str | None = None
|
|
41
|
+
run_id: str | None = None
|
|
42
|
+
diagnostics_summary: str | None = None
|
|
43
|
+
initial_compiler_passed: bool | None = None
|
|
44
|
+
artifact_paths: dict[str, str] = Field(default_factory=dict)
|
|
45
|
+
usage: dict[str, int | None] = Field(default_factory=dict)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ResponseObject(BaseModel):
|
|
49
|
+
id: str
|
|
50
|
+
object: Literal["response"] = "response"
|
|
51
|
+
status: Literal["completed", "failed"]
|
|
52
|
+
model: str
|
|
53
|
+
output_text: str | None = None
|
|
54
|
+
compiler_passed: bool | None = None
|
|
55
|
+
iterations_completed: int | None = None
|
|
56
|
+
started_at: str
|
|
57
|
+
finished_at: str | None = None
|
|
58
|
+
duration_ms: int | None = None
|
|
59
|
+
request_user_id: str
|
|
60
|
+
request_user_email: str | None = None
|
|
61
|
+
request_user_name: str | None = None
|
|
62
|
+
metadata: ResponseMetadata
|
|
63
|
+
error: ErrorPayload | None = None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class RepairObject(BaseModel):
|
|
67
|
+
id: str
|
|
68
|
+
object: Literal["repair"] = "repair"
|
|
69
|
+
status: Literal["completed", "failed"]
|
|
70
|
+
model: str
|
|
71
|
+
output_text: str | None = None
|
|
72
|
+
compiler_passed: bool | None = None
|
|
73
|
+
iterations_completed: int | None = None
|
|
74
|
+
started_at: str
|
|
75
|
+
finished_at: str | None = None
|
|
76
|
+
duration_ms: int | None = None
|
|
77
|
+
request_user_id: str
|
|
78
|
+
request_user_email: str | None = None
|
|
79
|
+
request_user_name: str | None = None
|
|
80
|
+
metadata: ResponseMetadata
|
|
81
|
+
error: ErrorPayload | None = None
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class RequestDetail(BaseModel):
|
|
85
|
+
id: str
|
|
86
|
+
object: Literal["request"] = "request"
|
|
87
|
+
status: str
|
|
88
|
+
model: str
|
|
89
|
+
output_text: str | None = None
|
|
90
|
+
compiler_passed: bool | None = None
|
|
91
|
+
iterations_completed: int | None = None
|
|
92
|
+
started_at: str
|
|
93
|
+
finished_at: str | None = None
|
|
94
|
+
duration_ms: int | None = None
|
|
95
|
+
request_user_id: str
|
|
96
|
+
request_user_email: str | None = None
|
|
97
|
+
request_user_name: str | None = None
|
|
98
|
+
metadata: ResponseMetadata
|
|
99
|
+
error: ErrorPayload | None = None
|
|
100
|
+
artifacts: list[ArtifactReference] = Field(default_factory=list)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class DependencyStatus(BaseModel):
|
|
104
|
+
available: bool
|
|
105
|
+
detail: str | None = None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class HealthResponse(BaseModel):
|
|
109
|
+
status: Literal["ok"] = "ok"
|
|
110
|
+
service: str
|
|
111
|
+
version: str
|
|
112
|
+
dependencies: dict[str, DependencyStatus]
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sysmlv2copilot
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python client SDK and CLI for the SysML refinement API
|
|
5
|
+
Author: Chance LaVoie
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/cmuchancel/sysmlv2copilot
|
|
8
|
+
Project-URL: Repository, https://github.com/cmuchancel/sysmlv2copilot
|
|
9
|
+
Project-URL: Issues, https://github.com/cmuchancel/sysmlv2copilot/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/cmuchancel/sysmlv2copilot/tree/main/docs
|
|
11
|
+
Keywords: sysml,sysmlv2,api-client,sdk,llm
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: httpx<1,>=0.28
|
|
22
|
+
Requires-Dist: pydantic<3,>=2.7
|
|
23
|
+
Provides-Extra: server
|
|
24
|
+
Requires-Dist: alembic<2,>=1.14; extra == "server"
|
|
25
|
+
Requires-Dist: anthropic<1,>=0.84; extra == "server"
|
|
26
|
+
Requires-Dist: fastapi<1,>=0.116; extra == "server"
|
|
27
|
+
Requires-Dist: openai<2,>=1.0; extra == "server"
|
|
28
|
+
Requires-Dist: psycopg[binary]<4,>=3.2; extra == "server"
|
|
29
|
+
Requires-Dist: pydantic-settings<3,>=2.7; extra == "server"
|
|
30
|
+
Requires-Dist: python-dotenv<2,>=1.0; extra == "server"
|
|
31
|
+
Requires-Dist: sqlalchemy<3,>=2.0; extra == "server"
|
|
32
|
+
Requires-Dist: uvicorn<1,>=0.35; extra == "server"
|
|
33
|
+
Provides-Extra: admin
|
|
34
|
+
Requires-Dist: pandas<3,>=2.2; extra == "admin"
|
|
35
|
+
Requires-Dist: streamlit<2,>=1.45; extra == "admin"
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: build<2,>=1.2; extra == "dev"
|
|
38
|
+
Requires-Dist: pytest<9,>=8.3; extra == "dev"
|
|
39
|
+
Requires-Dist: twine<7,>=5; extra == "dev"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# SysML Refinement API
|
|
43
|
+
|
|
44
|
+
This repository contains two distinct products:
|
|
45
|
+
- a public Python SDK and CLI, published as `sysmlv2copilot`
|
|
46
|
+
- an internal server/admin implementation that stays hosted and black-box
|
|
47
|
+
|
|
48
|
+
## Repo Split
|
|
49
|
+
|
|
50
|
+
The repository is now organized into two explicit halves:
|
|
51
|
+
- `api/`: index for the service/SDK/admin side
|
|
52
|
+
- `finetune/`: local dataset generation, repair corpus, and fine-tuning assets
|
|
53
|
+
|
|
54
|
+
To avoid breaking runtime imports in one large move, the API code still currently lives in the root-owned paths such as `app/`, `alembic/`, `sysmlv2copilot/`, `sysmlv2copilot_admin/`, and the main `scripts/` directory. The fine-tuning assets now live directly under `finetune/`.
|
|
55
|
+
|
|
56
|
+
The hosted service accepts either natural-language requirements or SysML input, runs the compiler-in-the-loop workflow, returns SysML plus structured metadata, and stores each request with a single persisted artifact bundle.
|
|
57
|
+
|
|
58
|
+
The service itself is intentionally narrow:
|
|
59
|
+
- internal-only
|
|
60
|
+
- one user, one API key
|
|
61
|
+
- plain text in, SysML text out
|
|
62
|
+
- FastAPI HTTP layer
|
|
63
|
+
- Postgres-compatible metadata storage
|
|
64
|
+
- filesystem artifact storage by default
|
|
65
|
+
- admin operations through CLI scripts, not public admin routes
|
|
66
|
+
|
|
67
|
+
## Public SDK
|
|
68
|
+
|
|
69
|
+
The public package is client-only. It does not ship the backend, migrations, deployment code, or admin console.
|
|
70
|
+
|
|
71
|
+
Install from PyPI:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
python -m pip install sysmlv2copilot
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Basic generation:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from sysmlv2copilot import SysMLCopilot
|
|
81
|
+
|
|
82
|
+
with SysMLCopilot(
|
|
83
|
+
api_key="sysml_live_...",
|
|
84
|
+
base_url="https://your-deployment.example.com",
|
|
85
|
+
) as client:
|
|
86
|
+
response = client.responses.create(
|
|
87
|
+
input="Design a compact warehouse inspection drone that can hover for 15 minutes.",
|
|
88
|
+
provider="openai",
|
|
89
|
+
)
|
|
90
|
+
print(response.id)
|
|
91
|
+
print(response.output_text)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Basic repair:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from sysmlv2copilot import SysMLCopilot
|
|
98
|
+
|
|
99
|
+
broken_sysml = """package Example {
|
|
100
|
+
public import ScalarValues::*;
|
|
101
|
+
requirement def R { text = "Missing semicolon" }
|
|
102
|
+
}
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
with SysMLCopilot(
|
|
106
|
+
api_key="sysml_live_...",
|
|
107
|
+
base_url="https://your-deployment.example.com",
|
|
108
|
+
) as client:
|
|
109
|
+
repaired = client.repairs.create(input=broken_sysml, provider="openai")
|
|
110
|
+
print(repaired.compiler_passed)
|
|
111
|
+
print(repaired.output_text)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
CLI examples:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
sysmlv2copilot --api-key sysml_live_... \
|
|
118
|
+
--base-url https://your-deployment.example.com \
|
|
119
|
+
responses create \
|
|
120
|
+
--input "Design a compact warehouse inspection drone that can hover for 15 minutes."
|
|
121
|
+
|
|
122
|
+
sysmlv2copilot --api-key sysml_live_... \
|
|
123
|
+
--base-url https://your-deployment.example.com \
|
|
124
|
+
repairs create \
|
|
125
|
+
--input-file ./broken.sysml
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
SDK details:
|
|
129
|
+
- [docs/PYTHON_SDK.md](/Users/chancelavoie/Desktop/sysmlv2copilot/docs/PYTHON_SDK.md)
|
|
130
|
+
- [docs/PYPI_RELEASE.md](/Users/chancelavoie/Desktop/sysmlv2copilot/docs/PYPI_RELEASE.md)
|
|
131
|
+
|
|
132
|
+
## What It Solves
|
|
133
|
+
|
|
134
|
+
The upstream pipeline already knows how to:
|
|
135
|
+
- build the iterative refinement prompt
|
|
136
|
+
- call the OpenAI Responses API
|
|
137
|
+
- write every iteration prompt and candidate SysML file
|
|
138
|
+
- run `syside check`
|
|
139
|
+
- capture compiler diagnostics
|
|
140
|
+
- stop on success or refinement limits
|
|
141
|
+
|
|
142
|
+
This service wraps that exact behavior in an authenticated API with request tracking and persistent artifacts so an internal team can use it immediately.
|
|
143
|
+
|
|
144
|
+
The refinement loop now supports either upstream provider:
|
|
145
|
+
- OpenAI via the Responses API
|
|
146
|
+
- Anthropic via the Messages API
|
|
147
|
+
|
|
148
|
+
The public API surface stays narrow and OpenAI-style. Provider choice is an internal runtime option exposed as an extra request field.
|
|
149
|
+
|
|
150
|
+
## Repo Layout
|
|
151
|
+
|
|
152
|
+
- `app/`: FastAPI app, auth, DB models, storage, and upstream wrapper
|
|
153
|
+
- `app/upstream/refine_sysml.py`: vendored upstream refiner used as the behavior source of truth
|
|
154
|
+
- `context/upstream/`: tight context package copied from the upstream repo
|
|
155
|
+
- `scripts/`: admin CLI for user creation, API key reset, and usage reporting
|
|
156
|
+
- `alembic/`: schema migrations
|
|
157
|
+
- `sysmlv2copilot/`: public client SDK and CLI package
|
|
158
|
+
- `sysmlv2copilot_admin/`: internal Streamlit admin console
|
|
159
|
+
- `tests/`: API, CLI, and config coverage
|
|
160
|
+
|
|
161
|
+
## Internal Repo Setup
|
|
162
|
+
|
|
163
|
+
If you are working on the hosted backend from this repo, install the internal extras:
|
|
164
|
+
|
|
165
|
+
1. Create a virtual environment and install dependencies:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
python3 -m venv .venv
|
|
169
|
+
source .venv/bin/activate
|
|
170
|
+
python -m pip install --upgrade pip
|
|
171
|
+
python -m pip install -e ".[server,admin,dev]"
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
2. Create `.env`:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
cp .env.example .env
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
3. Fill in at least:
|
|
181
|
+
- one or both of `OPENAI_API_KEY` / `ANTHROPIC_API_KEY`
|
|
182
|
+
- one of `SYSIDE_VENV_PATH` or `SYSIDE_EXECUTABLE_PATH`
|
|
183
|
+
- `API_KEY_HASH_PEPPER`
|
|
184
|
+
- optionally a PostgreSQL `DATABASE_URL`
|
|
185
|
+
|
|
186
|
+
SQLite works for local development. PostgreSQL or Supabase Postgres is preferred for shared environments.
|
|
187
|
+
For PostgreSQL or Supabase, install/runtime support now includes `psycopg`.
|
|
188
|
+
|
|
189
|
+
## Migrations
|
|
190
|
+
|
|
191
|
+
Run the schema migration before starting the service:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
alembic upgrade head
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Start The Service
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
uvicorn app.main:app --reload
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
The default address is `http://127.0.0.1:8000`.
|
|
204
|
+
|
|
205
|
+
## Create A User And API Key
|
|
206
|
+
|
|
207
|
+
Create a user and print the plaintext key once:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
python scripts/create_user.py --email engineer@example.com --name "Internal Engineer"
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Reset the single API key for an existing user:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
python scripts/reset_api_key.py --email engineer@example.com
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
List usage:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
python scripts/list_usage.py --email engineer@example.com --from-date 2026-03-01 --to-date 2026-03-31
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Call The API
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
curl -s http://127.0.0.1:8000/v1/responses \
|
|
229
|
+
-H "Authorization: Bearer sysml_live_..." \
|
|
230
|
+
-H "Content-Type: application/json" \
|
|
231
|
+
-d '{
|
|
232
|
+
"input": "Design a compact battery-powered inspection drone that fits in a 30 cm cube and flies for 20 minutes.",
|
|
233
|
+
"model": "refine-sysml-v1",
|
|
234
|
+
"provider": "openai",
|
|
235
|
+
"max_iters": 10,
|
|
236
|
+
"max_total_tokens": 40000,
|
|
237
|
+
"temperature": null
|
|
238
|
+
}'
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Example successful response:
|
|
242
|
+
|
|
243
|
+
```json
|
|
244
|
+
{
|
|
245
|
+
"id": "resp_20260312_161200_abcd",
|
|
246
|
+
"object": "response",
|
|
247
|
+
"status": "completed",
|
|
248
|
+
"model": "refine-sysml-v1",
|
|
249
|
+
"output_text": "package RequirementsOnly { ... }",
|
|
250
|
+
"compiler_passed": true,
|
|
251
|
+
"iterations_completed": 4,
|
|
252
|
+
"started_at": "2026-03-12T16:12:00Z",
|
|
253
|
+
"finished_at": "2026-03-12T16:12:22Z",
|
|
254
|
+
"duration_ms": 22014,
|
|
255
|
+
"request_user_id": "usr_1234",
|
|
256
|
+
"metadata": {
|
|
257
|
+
"provider": "openai",
|
|
258
|
+
"upstream_model": "gpt-5-mini",
|
|
259
|
+
"run_id": "run_20260312_161200_abcd",
|
|
260
|
+
"artifact_paths": {
|
|
261
|
+
"request_artifact": "/abs/path/request_artifact.json"
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
"error": null
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Repair request example:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
curl -s http://127.0.0.1:8000/v1/repairs \
|
|
272
|
+
-H "Authorization: Bearer sysml_live_..." \
|
|
273
|
+
-H "Content-Type: application/json" \
|
|
274
|
+
-d '{
|
|
275
|
+
"input": "package Example { requirement def R { text = \"Missing semicolon\" } }",
|
|
276
|
+
"model": "refine-sysml-v1",
|
|
277
|
+
"provider": "openai"
|
|
278
|
+
}'
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Anthropic request example:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
curl -s http://127.0.0.1:8000/v1/responses \
|
|
285
|
+
-H "Authorization: Bearer sysml_live_..." \
|
|
286
|
+
-H "Content-Type: application/json" \
|
|
287
|
+
-d '{
|
|
288
|
+
"input": "Design a compact battery-powered inspection drone that fits in a 30 cm cube and flies for 20 minutes.",
|
|
289
|
+
"model": "refine-sysml-v1",
|
|
290
|
+
"provider": "anthropic",
|
|
291
|
+
"upstream_model": "claude-sonnet-4-5-20250929"
|
|
292
|
+
}'
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Check a stored request:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
curl -s http://127.0.0.1:8000/v1/requests/resp_20260312_161200_abcd \
|
|
299
|
+
-H "Authorization: Bearer sysml_live_..."
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Health check:
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
curl -s http://127.0.0.1:8000/healthz
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Repair Workflow
|
|
309
|
+
|
|
310
|
+
The hosted API now supports a repair-oriented workflow:
|
|
311
|
+
- accept raw SysML text
|
|
312
|
+
- run SysIDE immediately
|
|
313
|
+
- if the model already passes, return it unchanged with `iterations_completed = 0`
|
|
314
|
+
- if it fails, feed the broken SysML plus real compiler diagnostics into the repair loop
|
|
315
|
+
- persist the same request tracking and single-artifact bundle used by generation requests
|
|
316
|
+
|
|
317
|
+
Repair metadata includes:
|
|
318
|
+
- `metadata.operation = "repair"`
|
|
319
|
+
- `metadata.initial_compiler_passed`
|
|
320
|
+
- `metadata.diagnostics_summary`
|
|
321
|
+
|
|
322
|
+
Design note:
|
|
323
|
+
- [docs/ITERATION_STALL_POLICY.md](/Users/chancelavoie/Desktop/sysmlv2copilot/docs/ITERATION_STALL_POLICY.md)
|
|
324
|
+
|
|
325
|
+
## Admin Frontend
|
|
326
|
+
|
|
327
|
+
Run the Streamlit admin console:
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
streamlit run sysmlv2copilot_admin/app.py
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
It uses the real configured database and supports:
|
|
334
|
+
- create users and issue API keys
|
|
335
|
+
- rotate existing keys
|
|
336
|
+
- inspect per-user usage totals
|
|
337
|
+
- inspect overall usage and compiler performance
|
|
338
|
+
- browse recent request activity
|
|
339
|
+
- check live API health against a configured base URL
|
|
340
|
+
|
|
341
|
+
More detail:
|
|
342
|
+
- [docs/ADMIN_FRONTEND.md](/Users/chancelavoie/Desktop/sysmlv2copilot/docs/ADMIN_FRONTEND.md)
|
|
343
|
+
- [docs/PYPI_RELEASE.md](/Users/chancelavoie/Desktop/sysmlv2copilot/docs/PYPI_RELEASE.md)
|
|
344
|
+
|
|
345
|
+
## Artifact Storage
|
|
346
|
+
|
|
347
|
+
Artifacts are stored under `ARTIFACT_ROOT`, defaulting to `./data/artifacts`. Each request gets a run directory with a single persisted artifact:
|
|
348
|
+
- `request_artifact.json`
|
|
349
|
+
|
|
350
|
+
That JSON bundle stores:
|
|
351
|
+
- the original input text
|
|
352
|
+
- the final SysML output
|
|
353
|
+
- request statistics such as tokens, chars, duration, and iterations
|
|
354
|
+
- provider/model metadata and any terminal error
|
|
355
|
+
|
|
356
|
+
Each request registers exactly one row in the `artifacts` table. Temporary iteration files used during SysIDE checks are removed before persistence.
|
|
357
|
+
|
|
358
|
+
## Tests
|
|
359
|
+
|
|
360
|
+
Run the test suite with:
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
pytest
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
The tests mock the expensive upstream generation path and cover auth, request tracking, repair flow, artifacts, CLI operations, health reporting, packaging boundaries, and config validation.
|
|
367
|
+
|
|
368
|
+
## Docker
|
|
369
|
+
|
|
370
|
+
Build:
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
docker build \
|
|
374
|
+
--build-arg SYSIDE_PIP_SPEC='syside==<your-version>' \
|
|
375
|
+
--build-arg REQUIRE_SYSIDE=1 \
|
|
376
|
+
-t sysml-refinement-api .
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Run:
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
docker run --rm -p 8000:8000 --env-file .env sysml-refinement-api
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
The container runs `alembic upgrade head` before starting `uvicorn`.
|
|
386
|
+
|
|
387
|
+
## Azure And Supabase
|
|
388
|
+
|
|
389
|
+
The repo is now prepared for:
|
|
390
|
+
- Azure Container Apps for compute
|
|
391
|
+
- Azure Container Registry for the private image
|
|
392
|
+
- Azure Files for persistent artifacts
|
|
393
|
+
- Supabase Postgres for metadata
|
|
394
|
+
|
|
395
|
+
Relevant files:
|
|
396
|
+
- [docs/AZURE_CONTAINER_APPS.md](/Users/chancelavoie/Desktop/sysmlv2copilot/docs/AZURE_CONTAINER_APPS.md)
|
|
397
|
+
- [scripts/deploy_azure_container_app.sh](/Users/chancelavoie/Desktop/sysmlv2copilot/scripts/deploy_azure_container_app.sh)
|
|
398
|
+
- [scripts/build_database_url.py](/Users/chancelavoie/Desktop/sysmlv2copilot/scripts/build_database_url.py)
|
|
399
|
+
- [scripts/render_azure_containerapp_yaml.py](/Users/chancelavoie/Desktop/sysmlv2copilot/scripts/render_azure_containerapp_yaml.py)
|
|
400
|
+
- [infra/supabase/create_service_role.sql](/Users/chancelavoie/Desktop/sysmlv2copilot/infra/supabase/create_service_role.sql)
|
|
401
|
+
|
|
402
|
+
To build a Supabase-compatible SQLAlchemy URL:
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
python scripts/build_database_url.py \
|
|
406
|
+
--host db.<project-ref>.supabase.co \
|
|
407
|
+
--port 5432 \
|
|
408
|
+
--database postgres \
|
|
409
|
+
--user sysml_api_service \
|
|
410
|
+
--password 'replace-me'
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
To deploy to Azure Container Apps:
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
export BUILD_MODE=local-docker
|
|
417
|
+
bash scripts/deploy_azure_container_app.sh
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## Upstream Context
|
|
421
|
+
|
|
422
|
+
The copied upstream context package lives in:
|
|
423
|
+
- `context/upstream/refine_sysml.py`
|
|
424
|
+
- `context/upstream/setup_pipeline_env.sh`
|
|
425
|
+
- `context/upstream/pipeline_README.md`
|
|
426
|
+
- `context/upstream/pipeline_HELP.md`
|
|
427
|
+
- `context/upstream/env.example`
|
|
428
|
+
- `context/examples/example_prompt.txt`
|
|
429
|
+
- `context/examples/example_output.sysml`
|
|
430
|
+
- `context/examples/example_run_log.json`
|
|
431
|
+
|
|
432
|
+
That context is intentionally tight and keeps the original refinement flow visible without copying the whole older repository.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
sysmlv2copilot/__init__.py,sha256=p2Mh-FKAw1Kttgd5uzR-mVXziyrfrS3nB2q6at6GFcQ,1049
|
|
2
|
+
sysmlv2copilot/__main__.py,sha256=PSQ4rpL0dG6f-qH4N7H-gD9igQkdHzH4yVZDcW8lfZo,80
|
|
3
|
+
sysmlv2copilot/cli.py,sha256=NgYQfYd0bgBjCKcqJdAqIZaf8LM8YxmMQXNfu3aiuKQ,5600
|
|
4
|
+
sysmlv2copilot/client.py,sha256=9haAmRTDyJ0sPeQuSfZAD820ak4q_FiXLZ__c5DEwc8,6327
|
|
5
|
+
sysmlv2copilot/exceptions.py,sha256=CciML13L2vi0ykFLBVKvnykCayo_lJESqSPW9yvDHo0,893
|
|
6
|
+
sysmlv2copilot/types.py,sha256=4UC0ln0kpjwgxKpFpxw5o1A5zW6PYheOXQB7ESb5iII,3097
|
|
7
|
+
sysmlv2copilot-0.2.0.dist-info/licenses/LICENSE,sha256=9If2iTgJ6D92J8kGny9XF5J_30GCiu_OCmu7rh0pQRQ,1070
|
|
8
|
+
sysmlv2copilot-0.2.0.dist-info/METADATA,sha256=GlA-RSzUKNv5FcLY8AnLG-4_yYx-56Hp1N-HgpZBVzM,13263
|
|
9
|
+
sysmlv2copilot-0.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
10
|
+
sysmlv2copilot-0.2.0.dist-info/entry_points.txt,sha256=kvZa4fSgRoJjdaBg3xyyenQhULsfLQsqAKv9uilcaWw,59
|
|
11
|
+
sysmlv2copilot-0.2.0.dist-info/top_level.txt,sha256=FNO1KVjFkDTO74Lbs9Zyb5yDAmcvleiZ50ap4sPaNGU,15
|
|
12
|
+
sysmlv2copilot-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Chance LaVoie
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sysmlv2copilot
|