quapp-common 0.0.13.dev8__tar.gz → 0.0.14.dev1__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.
- {quapp_common-0.0.13.dev8/quapp_common.egg-info → quapp_common-0.0.14.dev1}/PKG-INFO +135 -1
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/README.md +134 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/pyproject.toml +1 -1
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/async_tasks/async_invocation_task.py +11 -7
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/async_tasks/post_processing_task.py +13 -4
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/backend/invocation.py +24 -9
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/backend/job_fetcher.py +12 -5
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/backend/job_fetching.py +20 -12
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/bridge.py +18 -20
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/device/device_selection.py +15 -5
- quapp_common-0.0.14.dev1/quapp_common/component/dispatcher.py +272 -0
- quapp_common-0.0.14.dev1/quapp_common/config/log_context.py +186 -0
- quapp_common-0.0.14.dev1/quapp_common/config/logging_config.py +304 -0
- quapp_common-0.0.14.dev1/quapp_common/config/thread_config.py +28 -0
- quapp_common-0.0.14.dev1/quapp_common/data/response/error_report.py +191 -0
- quapp_common-0.0.14.dev1/quapp_common/enum/error_category.py +91 -0
- quapp_common-0.0.14.dev1/quapp_common/enum/runtime_phase.py +118 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/handler/handler.py +6 -1
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/model/device/custom_device.py +7 -2
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/model/device/device.py +62 -35
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/model/invocation.py +13 -1
- quapp_common-0.0.14.dev1/quapp_common/util/error_classifier.py +456 -0
- quapp_common-0.0.14.dev1/quapp_common/util/invocation_failure.py +103 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/util/response_utils.py +75 -19
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1/quapp_common.egg-info}/PKG-INFO +135 -1
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common.egg-info/SOURCES.txt +11 -0
- quapp_common-0.0.14.dev1/tests/test_dispatcher_runner_error.py +196 -0
- quapp_common-0.0.14.dev1/tests/test_error_classifier.py +382 -0
- quapp_common-0.0.14.dev1/tests/test_error_report.py +405 -0
- quapp_common-0.0.14.dev1/tests/test_invocation_failure.py +148 -0
- quapp_common-0.0.14.dev1/tests/test_logging_config.py +297 -0
- quapp_common-0.0.13.dev8/quapp_common/component/dispatcher.py +0 -123
- quapp_common-0.0.13.dev8/quapp_common/config/logging_config.py +0 -33
- quapp_common-0.0.13.dev8/quapp_common/config/thread_config.py +0 -10
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/LICENSE +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/async_tasks/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/async_tasks/async_task.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/async_tasks/export_circuit_task.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/backend/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/backend/job_heartbeat.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/backend/job_manager.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/callback/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/callback/update_job_metadata.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/circuit_adapter.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/device/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/result_serializer.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/config/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/async_task/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/async_task/circuit_export/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/async_task/circuit_export/backend_holder.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/async_task/circuit_export/circuit_holder.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/backend/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/backend/backend_information.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/callback/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/callback/callback_url.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/device/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/device/circuit_running_option.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/promise/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/promise/post_processing_promise.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/promise/promise.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/request/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/request/invocation_request.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/request/job_fetching_request.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/request/request.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/response/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/response/authentication.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/response/custom_header.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/data/response/job_response.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/enum/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/enum/base_enum.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/enum/http_header.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/enum/invocation_step.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/enum/language.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/enum/media_type.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/enum/processing_unit.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/enum/provider_tag.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/enum/sdk.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/enum/status/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/enum/status/job_status.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/enum/status/status_code.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/enum/token_type.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/factory/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/factory/device_factory.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/factory/handler_factory.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/factory/provider_factory.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/handler/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/model/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/model/device/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/model/provider/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/model/provider/provider.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/util/__init__.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/util/file_utils.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/util/http_utils.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/util/json_parser_utils.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common.egg-info/dependency_links.txt +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common.egg-info/requires.txt +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common.egg-info/top_level.txt +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/setup.cfg +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/tests/test_circuit_adapter.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/tests/test_job_heartbeat.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/tests/test_json_parser_utils.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/tests/test_language_dispatcher.py +0 -0
- {quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/tests/test_result_serializer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quapp-common
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.14.dev1
|
|
4
4
|
Summary: Quapp common library supporting Quapp Platform for Quantum Computing
|
|
5
5
|
Author-email: "CITYNOW Co. Ltd. " <corp@citynow.vn>
|
|
6
6
|
License: The MIT License (MIT)
|
|
@@ -114,6 +114,140 @@ Notes:
|
|
|
114
114
|
|
|
115
115
|
## Recently Changes Highlights
|
|
116
116
|
|
|
117
|
+
### v0.0.14.dev1 — Error taxonomy & error envelope (Phase 1)
|
|
118
|
+
|
|
119
|
+
Tiếp nối Phase 0. Bổ sung phân loại lỗi có ngữ nghĩa và envelope dùng chung cho
|
|
120
|
+
Function Log lẫn Job Log. **Additive**: `job_result` chỉ thêm field `error`,
|
|
121
|
+
`message` và `exception` giữ nguyên; contract callback URL không đổi.
|
|
122
|
+
|
|
123
|
+
**Taxonomy — hai chiều phân loại độc lập**
|
|
124
|
+
- `enum/error_category.py`: `ErrorCategory` 13 giá trị, nhóm theo *ai xử lý
|
|
125
|
+
được* (`owner_of()` → USER / WORKSPACE_ADMIN / PROVIDER / PLATFORM).
|
|
126
|
+
- `enum/runtime_phase.py`: `RuntimePhase` 9 giá trị. Tách được
|
|
127
|
+
`PROVIDER_INVOCATION` / `COMPILATION` / `EXECUTION` — trước đây auth sai,
|
|
128
|
+
transpile fail và provider từ chối job đều ra chung nhãn `EXECUTION`.
|
|
129
|
+
`PHASE_TO_STEP` giữ nguyên contract `InvocationStep` của callback URL.
|
|
130
|
+
|
|
131
|
+
**Classifier (`util/error_classifier.py`)** — tổ hợp 5 tín hiệu theo độ tin cậy:
|
|
132
|
+
1. `phase` — biết chắc từ context manager, không suy đoán;
|
|
133
|
+
2. `frame_owner()` — code của ai, dựa vào đường dẫn frame sâu nhất
|
|
134
|
+
(`function/` = user, `quapp_*` = platform, `site-packages/` = SDK);
|
|
135
|
+
3. tên class exception, đi hết **cause chain** qua cả `__cause__` *và*
|
|
136
|
+
`__context__` — cần thiết vì nhiều provider lib bọc lỗi bằng
|
|
137
|
+
`raise ValueError(...)` trong khối `except` mà không có `from`;
|
|
138
|
+
4. attribute đọc được: HTTP status, AWS error code, exit code, signal;
|
|
139
|
+
5. message pattern (dùng cuối cùng).
|
|
140
|
+
|
|
141
|
+
`register_rules()` cho phép lib provider đăng ký rule đặc thù SDK khi import,
|
|
142
|
+
nên `qapp-common` không phải import qiskit/braket. Batch đăng ký sau được xét
|
|
143
|
+
trước; trong cùng batch giữ nguyên thứ tự. Rule ném lỗi bị bỏ qua chứ không làm
|
|
144
|
+
hỏng đường báo lỗi. 20 rule generic sẵn có.
|
|
145
|
+
|
|
146
|
+
Fallback nhiều tầng nên **không bao giờ trả `UNKNOWN` một cách mù**: không rule
|
|
147
|
+
nào khớp thì suy từ frame ownership, rồi tới phase.
|
|
148
|
+
|
|
149
|
+
**Envelope (`data/response/error_report.py`)** — `job_result['error']` gồm
|
|
150
|
+
`code`, `category`, `phase`, `owner`, `retryable`, `rootCause` (type + detail +
|
|
151
|
+
file:line), `causeChain`, `stackTrace` (full, đã redact),
|
|
152
|
+
`handlerStackTrace` (stack JS/.NET cho handler không phải Python),
|
|
153
|
+
`context`, `resolution` (summary + steps + docUrl), `retry`, `traceId`.
|
|
154
|
+
`log_fields()` bind cùng vocabulary vào Function Log.
|
|
155
|
+
|
|
156
|
+
**Timeline (`config/log_context.py`)** — context manager `phase()` phát
|
|
157
|
+
`phase_start` / `phase_end` / `phase_failed` kèm `duration_ms`. Đây là dữ liệu
|
|
158
|
+
để UI vẽ timeline và chỉ ra job chết ở bước nào. `phase_failed` ở mức WARNING để
|
|
159
|
+
thấy được khi chạy `LOG_LEVEL=INFO`.
|
|
160
|
+
|
|
161
|
+
`_FAILED_PHASE` ghi lại phase mà exception thoát ra: khối `except` xử lý lỗi gần
|
|
162
|
+
như luôn nằm **ngoài** `with phase(...)`, lúc đó phase đang mở đã bị reset trong
|
|
163
|
+
`finally`. Không có nó thì mọi `ErrorReport` đều `phase=None` và fallback theo
|
|
164
|
+
phase không bao giờ chạy. Dùng `effective_phase()` ở đường báo lỗi.
|
|
165
|
+
|
|
166
|
+
**`config/thread_config.py`** — `ContextAwareThreadPoolExecutor` copy contextvars
|
|
167
|
+
khi submit. `ThreadPoolExecutor` thuần không làm việc này (khác
|
|
168
|
+
`asyncio.to_thread`), nên analysis + finalization chạy trong
|
|
169
|
+
`circuit_running_pool` sẽ mất `job_id` / `trace_id` / `phase`.
|
|
170
|
+
|
|
171
|
+
**Call-site** — `phase()` bọc PREPROCESSING / DEVICE_SELECTION /
|
|
172
|
+
PROVIDER_INVOCATION / COMPILATION / EXECUTION / ANALYSIS; toàn bộ 13 call-site
|
|
173
|
+
của `build_error_job_response()` truyền `phase=`.
|
|
174
|
+
|
|
175
|
+
**Test** — `tests/test_error_classifier.py` (52 case: cause chain, HTTP/AWS
|
|
176
|
+
code, frame ownership, phase fallback, rule ordering) và
|
|
177
|
+
`tests/test_error_report.py` (44 case: envelope, timeline, thread pool).
|
|
178
|
+
|
|
179
|
+
### v0.0.13.dev9 — Error logging & observability (Phase 0)
|
|
180
|
+
|
|
181
|
+
Mục tiêu: Function Log và Job Log phải chỉ ra được nguyên nhân lỗi. Toàn bộ thay
|
|
182
|
+
đổi trong bản này là **additive hoặc bug fix** — không đổi contract callback,
|
|
183
|
+
không đổi schema `job_result`, không cần frontend/backend sửa gì.
|
|
184
|
+
|
|
185
|
+
**Logging (`config/`)**
|
|
186
|
+
- Thêm `config/log_context.py`: `bind_job()` / `enrich_job_context()` đặt
|
|
187
|
+
`job_id`, `trace_id`, `sdk`, `provider`, `device` vào `contextvars`. Mọi log
|
|
188
|
+
sau đó tự có đủ field, kể cả log của `Provider` / `DeviceSelection` /
|
|
189
|
+
`*Factory` — những chỗ trước đây rơi vào context `"QuappLibs"` nên không gắn
|
|
190
|
+
được vào job nào.
|
|
191
|
+
- `configure_logging()` **idempotent**. Trước đây `AsyncInvocationTask.do()` gọi
|
|
192
|
+
`logger.add(sink=sys.stderr, ...)` cho mỗi request, nên sau N job có N+1 sink
|
|
193
|
+
và mỗi dòng log in N+1 lần.
|
|
194
|
+
- Thêm `InterceptHandler` + `install_stdlib_intercept()`: đẩy mọi record của
|
|
195
|
+
stdlib `logging` sang loguru. Cần thiết vì 24/34 template trong
|
|
196
|
+
`quapp-sdk-templates` dùng `logging.getLogger('index')` mà không cấu hình
|
|
197
|
+
handler — `debug`/`info` của chúng bị drop im lặng và `exception` in ra không
|
|
198
|
+
có prefix `[ConsoleJobLog]` lẫn `job_id`. Log của uvicorn/qiskit/botocore cũng
|
|
199
|
+
về cùng một format.
|
|
200
|
+
- Thêm JSON sink cho structured logging, bật bằng `QUAPP_LOG_FORMAT=json|both`
|
|
201
|
+
(default `text`, giữ nguyên hành vi cũ). Field: `jobId`, `traceId`, `phase`,
|
|
202
|
+
`category`, `errorCode`, `sdk`, `provider`, `device`, `event`, `durationMs`.
|
|
203
|
+
- Thêm redaction filter: mask `token` / `apiKey` / `authentication` /
|
|
204
|
+
`clientSecret` / … trước khi ghi log, áp dụng cả cho stack trace.
|
|
205
|
+
- Fix: text formatter thiếu `{exception}`. Với formatter dạng callable, loguru
|
|
206
|
+
**không** tự append traceback — nên trước đây mọi `logger.exception()` trong
|
|
207
|
+
codebase không in ra stack trace nào.
|
|
208
|
+
- Fix: `depth` trong intercept khiến mọi record hiện là
|
|
209
|
+
`logging:callHandlers:<line>` thay vì file:line của nơi gọi log.
|
|
210
|
+
- Fix: với `enqueue=True`, traceback object không picklable nên bị thay bằng
|
|
211
|
+
`None` sau khi qua queue. Traceback nay được format sẵn ở filter.
|
|
212
|
+
|
|
213
|
+
**Error từ runner không phải Python (`component/dispatcher.py`, `bridge.py`)**
|
|
214
|
+
- Bỏ `subprocess.run(..., check=True)`. Nó raise `CalledProcessError` **trước
|
|
215
|
+
khi** đọc được output, làm nhánh `_extract_error_message()` thành dead code và
|
|
216
|
+
user chỉ thấy `Command '[...]' returned non-zero exit status 1`.
|
|
217
|
+
- Thêm `HandlerRuntimeError` mang `language`, `action`, `error_type`, `stack`
|
|
218
|
+
(stack của runtime gốc — JS/.NET), `exit_code`, `signal_name`. Sáu tình huống
|
|
219
|
+
được phân biệt: runner không có trong image, timeout, bị signal (handler.c
|
|
220
|
+
segfault → returncode âm, stdout rỗng), structured error trên stdout, exit
|
|
221
|
+
code khác 0, và output không phải JSON hợp lệ.
|
|
222
|
+
- Đọc được **cả contract cũ** (`error` dạng string, stack trên stderr) nên không
|
|
223
|
+
cần deploy đồng thời với template.
|
|
224
|
+
- `_format_exception()` đưa `handlerStackTrace` / `language` / `action` /
|
|
225
|
+
`exitCode` / `signal` lên `job_result` cho lỗi handler JS/Q#/C.
|
|
226
|
+
|
|
227
|
+
**Đường lỗi (`model/device/device.py`, `util/response_utils.py`, …)**
|
|
228
|
+
- `Device._on_execution` dùng `build_error_job_response()` thay cho
|
|
229
|
+
`job_result = {"error": str(exception)}` và log ở `ERROR` thay vì `DEBUG`.
|
|
230
|
+
Đây là đường lỗi quan trọng nhất (submit circuit lên device) và trước đây là
|
|
231
|
+
đường mất thông tin nhiều nhất.
|
|
232
|
+
- `build_error_job_response()` nay tự phát một log `ERROR` duy nhất cho mọi lỗi,
|
|
233
|
+
nên Job Log và Function Log không còn lệch nhau.
|
|
234
|
+
- Truyền `stage` cho 8 call-site còn thiếu (trước đây 10/13 call-site rơi vào
|
|
235
|
+
default `'while running your job'`).
|
|
236
|
+
- Fix `generate_response()`: nhánh `else` truy cập `job_response.status_code`
|
|
237
|
+
khi `job_response` là falsy → `AttributeError`, thay lỗi gốc bằng lỗi khác.
|
|
238
|
+
- Fix `job_fetching.py`: `__produce_histogram_data` / `__get_execution_time`
|
|
239
|
+
khai báo `@staticmethod` nhưng dùng `self.logger` → `NameError` khi được gọi.
|
|
240
|
+
- `DeviceSelection`: không log nguyên response (chứa token provider) và không
|
|
241
|
+
nhét `response.content` thô vào exception (nội dung này chảy vào `job_result`
|
|
242
|
+
rồi hiện lên UI).
|
|
243
|
+
- `PostProcessingTask`: bỏ `print()` ghi thẳng ra stdout (không qua sink nên
|
|
244
|
+
không được redact và không có `job_id`).
|
|
245
|
+
|
|
246
|
+
**Test**
|
|
247
|
+
- `tests/test_logging_config.py` — redaction, idempotent sink, job context,
|
|
248
|
+
intercept stdlib, exception payload.
|
|
249
|
+
- `tests/test_dispatcher_runner_error.py` — 6 nhánh phân loại lỗi runner.
|
|
250
|
+
|
|
117
251
|
### v0.0.13.dev2
|
|
118
252
|
- Fix: `_openqasm_to_braket` in `circuit_adapter.py` now injects a default
|
|
119
253
|
`Probability` result type when the Braket circuit produced by `tk_to_braket`
|
|
@@ -73,6 +73,140 @@ Notes:
|
|
|
73
73
|
|
|
74
74
|
## Recently Changes Highlights
|
|
75
75
|
|
|
76
|
+
### v0.0.14.dev1 — Error taxonomy & error envelope (Phase 1)
|
|
77
|
+
|
|
78
|
+
Tiếp nối Phase 0. Bổ sung phân loại lỗi có ngữ nghĩa và envelope dùng chung cho
|
|
79
|
+
Function Log lẫn Job Log. **Additive**: `job_result` chỉ thêm field `error`,
|
|
80
|
+
`message` và `exception` giữ nguyên; contract callback URL không đổi.
|
|
81
|
+
|
|
82
|
+
**Taxonomy — hai chiều phân loại độc lập**
|
|
83
|
+
- `enum/error_category.py`: `ErrorCategory` 13 giá trị, nhóm theo *ai xử lý
|
|
84
|
+
được* (`owner_of()` → USER / WORKSPACE_ADMIN / PROVIDER / PLATFORM).
|
|
85
|
+
- `enum/runtime_phase.py`: `RuntimePhase` 9 giá trị. Tách được
|
|
86
|
+
`PROVIDER_INVOCATION` / `COMPILATION` / `EXECUTION` — trước đây auth sai,
|
|
87
|
+
transpile fail và provider từ chối job đều ra chung nhãn `EXECUTION`.
|
|
88
|
+
`PHASE_TO_STEP` giữ nguyên contract `InvocationStep` của callback URL.
|
|
89
|
+
|
|
90
|
+
**Classifier (`util/error_classifier.py`)** — tổ hợp 5 tín hiệu theo độ tin cậy:
|
|
91
|
+
1. `phase` — biết chắc từ context manager, không suy đoán;
|
|
92
|
+
2. `frame_owner()` — code của ai, dựa vào đường dẫn frame sâu nhất
|
|
93
|
+
(`function/` = user, `quapp_*` = platform, `site-packages/` = SDK);
|
|
94
|
+
3. tên class exception, đi hết **cause chain** qua cả `__cause__` *và*
|
|
95
|
+
`__context__` — cần thiết vì nhiều provider lib bọc lỗi bằng
|
|
96
|
+
`raise ValueError(...)` trong khối `except` mà không có `from`;
|
|
97
|
+
4. attribute đọc được: HTTP status, AWS error code, exit code, signal;
|
|
98
|
+
5. message pattern (dùng cuối cùng).
|
|
99
|
+
|
|
100
|
+
`register_rules()` cho phép lib provider đăng ký rule đặc thù SDK khi import,
|
|
101
|
+
nên `qapp-common` không phải import qiskit/braket. Batch đăng ký sau được xét
|
|
102
|
+
trước; trong cùng batch giữ nguyên thứ tự. Rule ném lỗi bị bỏ qua chứ không làm
|
|
103
|
+
hỏng đường báo lỗi. 20 rule generic sẵn có.
|
|
104
|
+
|
|
105
|
+
Fallback nhiều tầng nên **không bao giờ trả `UNKNOWN` một cách mù**: không rule
|
|
106
|
+
nào khớp thì suy từ frame ownership, rồi tới phase.
|
|
107
|
+
|
|
108
|
+
**Envelope (`data/response/error_report.py`)** — `job_result['error']` gồm
|
|
109
|
+
`code`, `category`, `phase`, `owner`, `retryable`, `rootCause` (type + detail +
|
|
110
|
+
file:line), `causeChain`, `stackTrace` (full, đã redact),
|
|
111
|
+
`handlerStackTrace` (stack JS/.NET cho handler không phải Python),
|
|
112
|
+
`context`, `resolution` (summary + steps + docUrl), `retry`, `traceId`.
|
|
113
|
+
`log_fields()` bind cùng vocabulary vào Function Log.
|
|
114
|
+
|
|
115
|
+
**Timeline (`config/log_context.py`)** — context manager `phase()` phát
|
|
116
|
+
`phase_start` / `phase_end` / `phase_failed` kèm `duration_ms`. Đây là dữ liệu
|
|
117
|
+
để UI vẽ timeline và chỉ ra job chết ở bước nào. `phase_failed` ở mức WARNING để
|
|
118
|
+
thấy được khi chạy `LOG_LEVEL=INFO`.
|
|
119
|
+
|
|
120
|
+
`_FAILED_PHASE` ghi lại phase mà exception thoát ra: khối `except` xử lý lỗi gần
|
|
121
|
+
như luôn nằm **ngoài** `with phase(...)`, lúc đó phase đang mở đã bị reset trong
|
|
122
|
+
`finally`. Không có nó thì mọi `ErrorReport` đều `phase=None` và fallback theo
|
|
123
|
+
phase không bao giờ chạy. Dùng `effective_phase()` ở đường báo lỗi.
|
|
124
|
+
|
|
125
|
+
**`config/thread_config.py`** — `ContextAwareThreadPoolExecutor` copy contextvars
|
|
126
|
+
khi submit. `ThreadPoolExecutor` thuần không làm việc này (khác
|
|
127
|
+
`asyncio.to_thread`), nên analysis + finalization chạy trong
|
|
128
|
+
`circuit_running_pool` sẽ mất `job_id` / `trace_id` / `phase`.
|
|
129
|
+
|
|
130
|
+
**Call-site** — `phase()` bọc PREPROCESSING / DEVICE_SELECTION /
|
|
131
|
+
PROVIDER_INVOCATION / COMPILATION / EXECUTION / ANALYSIS; toàn bộ 13 call-site
|
|
132
|
+
của `build_error_job_response()` truyền `phase=`.
|
|
133
|
+
|
|
134
|
+
**Test** — `tests/test_error_classifier.py` (52 case: cause chain, HTTP/AWS
|
|
135
|
+
code, frame ownership, phase fallback, rule ordering) và
|
|
136
|
+
`tests/test_error_report.py` (44 case: envelope, timeline, thread pool).
|
|
137
|
+
|
|
138
|
+
### v0.0.13.dev9 — Error logging & observability (Phase 0)
|
|
139
|
+
|
|
140
|
+
Mục tiêu: Function Log và Job Log phải chỉ ra được nguyên nhân lỗi. Toàn bộ thay
|
|
141
|
+
đổi trong bản này là **additive hoặc bug fix** — không đổi contract callback,
|
|
142
|
+
không đổi schema `job_result`, không cần frontend/backend sửa gì.
|
|
143
|
+
|
|
144
|
+
**Logging (`config/`)**
|
|
145
|
+
- Thêm `config/log_context.py`: `bind_job()` / `enrich_job_context()` đặt
|
|
146
|
+
`job_id`, `trace_id`, `sdk`, `provider`, `device` vào `contextvars`. Mọi log
|
|
147
|
+
sau đó tự có đủ field, kể cả log của `Provider` / `DeviceSelection` /
|
|
148
|
+
`*Factory` — những chỗ trước đây rơi vào context `"QuappLibs"` nên không gắn
|
|
149
|
+
được vào job nào.
|
|
150
|
+
- `configure_logging()` **idempotent**. Trước đây `AsyncInvocationTask.do()` gọi
|
|
151
|
+
`logger.add(sink=sys.stderr, ...)` cho mỗi request, nên sau N job có N+1 sink
|
|
152
|
+
và mỗi dòng log in N+1 lần.
|
|
153
|
+
- Thêm `InterceptHandler` + `install_stdlib_intercept()`: đẩy mọi record của
|
|
154
|
+
stdlib `logging` sang loguru. Cần thiết vì 24/34 template trong
|
|
155
|
+
`quapp-sdk-templates` dùng `logging.getLogger('index')` mà không cấu hình
|
|
156
|
+
handler — `debug`/`info` của chúng bị drop im lặng và `exception` in ra không
|
|
157
|
+
có prefix `[ConsoleJobLog]` lẫn `job_id`. Log của uvicorn/qiskit/botocore cũng
|
|
158
|
+
về cùng một format.
|
|
159
|
+
- Thêm JSON sink cho structured logging, bật bằng `QUAPP_LOG_FORMAT=json|both`
|
|
160
|
+
(default `text`, giữ nguyên hành vi cũ). Field: `jobId`, `traceId`, `phase`,
|
|
161
|
+
`category`, `errorCode`, `sdk`, `provider`, `device`, `event`, `durationMs`.
|
|
162
|
+
- Thêm redaction filter: mask `token` / `apiKey` / `authentication` /
|
|
163
|
+
`clientSecret` / … trước khi ghi log, áp dụng cả cho stack trace.
|
|
164
|
+
- Fix: text formatter thiếu `{exception}`. Với formatter dạng callable, loguru
|
|
165
|
+
**không** tự append traceback — nên trước đây mọi `logger.exception()` trong
|
|
166
|
+
codebase không in ra stack trace nào.
|
|
167
|
+
- Fix: `depth` trong intercept khiến mọi record hiện là
|
|
168
|
+
`logging:callHandlers:<line>` thay vì file:line của nơi gọi log.
|
|
169
|
+
- Fix: với `enqueue=True`, traceback object không picklable nên bị thay bằng
|
|
170
|
+
`None` sau khi qua queue. Traceback nay được format sẵn ở filter.
|
|
171
|
+
|
|
172
|
+
**Error từ runner không phải Python (`component/dispatcher.py`, `bridge.py`)**
|
|
173
|
+
- Bỏ `subprocess.run(..., check=True)`. Nó raise `CalledProcessError` **trước
|
|
174
|
+
khi** đọc được output, làm nhánh `_extract_error_message()` thành dead code và
|
|
175
|
+
user chỉ thấy `Command '[...]' returned non-zero exit status 1`.
|
|
176
|
+
- Thêm `HandlerRuntimeError` mang `language`, `action`, `error_type`, `stack`
|
|
177
|
+
(stack của runtime gốc — JS/.NET), `exit_code`, `signal_name`. Sáu tình huống
|
|
178
|
+
được phân biệt: runner không có trong image, timeout, bị signal (handler.c
|
|
179
|
+
segfault → returncode âm, stdout rỗng), structured error trên stdout, exit
|
|
180
|
+
code khác 0, và output không phải JSON hợp lệ.
|
|
181
|
+
- Đọc được **cả contract cũ** (`error` dạng string, stack trên stderr) nên không
|
|
182
|
+
cần deploy đồng thời với template.
|
|
183
|
+
- `_format_exception()` đưa `handlerStackTrace` / `language` / `action` /
|
|
184
|
+
`exitCode` / `signal` lên `job_result` cho lỗi handler JS/Q#/C.
|
|
185
|
+
|
|
186
|
+
**Đường lỗi (`model/device/device.py`, `util/response_utils.py`, …)**
|
|
187
|
+
- `Device._on_execution` dùng `build_error_job_response()` thay cho
|
|
188
|
+
`job_result = {"error": str(exception)}` và log ở `ERROR` thay vì `DEBUG`.
|
|
189
|
+
Đây là đường lỗi quan trọng nhất (submit circuit lên device) và trước đây là
|
|
190
|
+
đường mất thông tin nhiều nhất.
|
|
191
|
+
- `build_error_job_response()` nay tự phát một log `ERROR` duy nhất cho mọi lỗi,
|
|
192
|
+
nên Job Log và Function Log không còn lệch nhau.
|
|
193
|
+
- Truyền `stage` cho 8 call-site còn thiếu (trước đây 10/13 call-site rơi vào
|
|
194
|
+
default `'while running your job'`).
|
|
195
|
+
- Fix `generate_response()`: nhánh `else` truy cập `job_response.status_code`
|
|
196
|
+
khi `job_response` là falsy → `AttributeError`, thay lỗi gốc bằng lỗi khác.
|
|
197
|
+
- Fix `job_fetching.py`: `__produce_histogram_data` / `__get_execution_time`
|
|
198
|
+
khai báo `@staticmethod` nhưng dùng `self.logger` → `NameError` khi được gọi.
|
|
199
|
+
- `DeviceSelection`: không log nguyên response (chứa token provider) và không
|
|
200
|
+
nhét `response.content` thô vào exception (nội dung này chảy vào `job_result`
|
|
201
|
+
rồi hiện lên UI).
|
|
202
|
+
- `PostProcessingTask`: bỏ `print()` ghi thẳng ra stdout (không qua sink nên
|
|
203
|
+
không được redact và không có `job_id`).
|
|
204
|
+
|
|
205
|
+
**Test**
|
|
206
|
+
- `tests/test_logging_config.py` — redaction, idempotent sink, job context,
|
|
207
|
+
intercept stdlib, exception payload.
|
|
208
|
+
- `tests/test_dispatcher_runner_error.py` — 6 nhánh phân loại lỗi runner.
|
|
209
|
+
|
|
76
210
|
### v0.0.13.dev2
|
|
77
211
|
- Fix: `_openqasm_to_braket` in `circuit_adapter.py` now injects a default
|
|
78
212
|
`Probability` result type when the Braket circuit produced by `tk_to_braket`
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "quapp-common"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.14.dev1"
|
|
8
8
|
description = "Quapp common library supporting Quapp Platform for Quantum Computing"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "CITYNOW Co. Ltd. ", email = "corp@citynow.vn" }]
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
# async_invocation_task.py
|
|
3
3
|
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
4
|
import asyncio
|
|
5
|
-
import sys
|
|
6
5
|
|
|
7
6
|
from starlette.concurrency import run_in_threadpool
|
|
8
7
|
from starlette.responses import JSONResponse
|
|
@@ -10,6 +9,7 @@ from starlette.responses import JSONResponse
|
|
|
10
9
|
from ..async_tasks.async_task import AsyncTask
|
|
11
10
|
from ..component.backend.job_manager import JobManager
|
|
12
11
|
from ..component.callback.update_job_metadata import update_job_metadata
|
|
12
|
+
from ..config.log_context import bind_job
|
|
13
13
|
from ..config.logging_config import logger
|
|
14
14
|
from ..factory.handler_factory import HandlerFactory
|
|
15
15
|
from ..model.invocation import Event, Error, Success
|
|
@@ -61,9 +61,12 @@ class AsyncInvocationTask(AsyncTask):
|
|
|
61
61
|
job_id = self.event.json().get('jobId')
|
|
62
62
|
if job_id is None:
|
|
63
63
|
raise Exception("Job ID is missing from event")
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
|
|
65
|
+
# Trước đây chỗ này gọi logger.add(sink=sys.stderr, ...) cho MỖI
|
|
66
|
+
# request: sau N job có N+1 sink và mỗi dòng log in N+1 lần. Sink đã
|
|
67
|
+
# được cấu hình một lần trong logging_config; ở đây chỉ cần bind
|
|
68
|
+
# context để mọi log sau đó tự có job_id/trace_id.
|
|
69
|
+
job_log = bind_job(job_id)
|
|
67
70
|
|
|
68
71
|
# Define the blocking work
|
|
69
72
|
def _run_handle():
|
|
@@ -78,7 +81,7 @@ class AsyncInvocationTask(AsyncTask):
|
|
|
78
81
|
if t.cancelled() or t.exception() is None:
|
|
79
82
|
return
|
|
80
83
|
exc = t.exception()
|
|
81
|
-
|
|
84
|
+
job_log.bind(event='background_task_failed').exception(
|
|
82
85
|
f"Background job task failed for job {job_id}: {exc}")
|
|
83
86
|
try:
|
|
84
87
|
event_data = self.event.json()
|
|
@@ -87,7 +90,8 @@ class AsyncInvocationTask(AsyncTask):
|
|
|
87
90
|
if on_error_url:
|
|
88
91
|
error_response = build_error_job_response(
|
|
89
92
|
exc,
|
|
90
|
-
message=f"Unhandled background task error: {exc}"
|
|
93
|
+
message=f"Unhandled background task error: {exc}",
|
|
94
|
+
stage='while running your job in the background')
|
|
91
95
|
error_response.user_token = event_data.get('userToken')
|
|
92
96
|
error_response.user_identity = event_data.get(
|
|
93
97
|
'userIdentity')
|
|
@@ -97,7 +101,7 @@ class AsyncInvocationTask(AsyncTask):
|
|
|
97
101
|
event_data, 'workspaceHeader')
|
|
98
102
|
update_job_metadata(error_response, on_error_url)
|
|
99
103
|
except Exception as fallback_exc:
|
|
100
|
-
|
|
104
|
+
job_log.bind(event='callback_failed').exception(
|
|
101
105
|
f"Failed to notify BE of error for job {job_id}: {fallback_exc}")
|
|
102
106
|
|
|
103
107
|
task.add_done_callback(_on_task_done)
|
|
@@ -5,6 +5,7 @@ from .async_task import AsyncTask
|
|
|
5
5
|
from ..config.logging_config import job_logger
|
|
6
6
|
from ..data.promise.post_processing_promise import PostProcessingPromise
|
|
7
7
|
from ..enum.media_type import MediaType
|
|
8
|
+
from ..enum.runtime_phase import RuntimePhase
|
|
8
9
|
from ..util.http_utils import get_job_id_from_url
|
|
9
10
|
from ..util.json_parser_utils import parse
|
|
10
11
|
|
|
@@ -35,15 +36,23 @@ class PostProcessingTask(AsyncTask):
|
|
|
35
36
|
job_result_post_processing = self.post_processing_fn(
|
|
36
37
|
self.promise.job_result)
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
# print() cũ ghi thẳng ra stdout, không qua sink nên không được
|
|
40
|
+
# redact và không có job_id.
|
|
41
|
+
self.logger.debug(
|
|
42
|
+
f"Parsing job result: type={type(job_result_post_processing).__name__}")
|
|
40
43
|
job_response.job_result = parse(job_result_post_processing)
|
|
41
44
|
|
|
42
45
|
update_job_metadata(job_response, self.promise.callback_url.on_done)
|
|
43
46
|
|
|
44
47
|
except Exception as exception:
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
self.logger.bind(event='job_failed').exception(
|
|
49
|
+
f"Post-processing failed: {exception}")
|
|
50
|
+
|
|
51
|
+
job_response = build_error_job_response(
|
|
52
|
+
exception, job_response,
|
|
53
|
+
message='Error when post processing job result',
|
|
54
|
+
stage='while post-processing the job result',
|
|
55
|
+
phase=RuntimePhase.POST_PROCESSING)
|
|
47
56
|
|
|
48
57
|
update_job_metadata(job_response,
|
|
49
58
|
self.promise.callback_url.on_error)
|
{quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/backend/invocation.py
RENAMED
|
@@ -6,6 +6,7 @@ from abc import ABC, abstractmethod
|
|
|
6
6
|
|
|
7
7
|
from ..callback.update_job_metadata import update_job_metadata
|
|
8
8
|
from ...component.device.device_selection import DeviceSelection
|
|
9
|
+
from ...config.log_context import bind_job, enrich_job_context, phase
|
|
9
10
|
from ...config.logging_config import job_logger
|
|
10
11
|
from ...config.thread_config import circuit_running_pool
|
|
11
12
|
from ...data.backend.backend_information import BackendInformation
|
|
@@ -15,6 +16,7 @@ from ...data.response.authentication import Authentication
|
|
|
15
16
|
from ...data.response.custom_header import CustomHeader
|
|
16
17
|
from ...data.response.job_response import JobResponse
|
|
17
18
|
from ...enum.invocation_step import InvocationStep
|
|
19
|
+
from ...enum.runtime_phase import RuntimePhase
|
|
18
20
|
from ...enum.sdk import Sdk
|
|
19
21
|
from ...enum.status.status_code import StatusCode
|
|
20
22
|
from ...model.provider.provider import Provider
|
|
@@ -82,8 +84,12 @@ class Invocation(ABC):
|
|
|
82
84
|
return None
|
|
83
85
|
|
|
84
86
|
try:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
with phase(RuntimePhase.DEVICE_SELECTION):
|
|
88
|
+
self.__prepare_backend_data(circuit)
|
|
89
|
+
|
|
90
|
+
enrich_job_context(
|
|
91
|
+
device=self.backend_information.device_name,
|
|
92
|
+
provider=self.backend_information.provider_tag.value)
|
|
87
93
|
self.logger.info(
|
|
88
94
|
f"Backend data prepared: device {self.backend_information.device_name}, provider {self.backend_information.provider_tag.value}")
|
|
89
95
|
|
|
@@ -94,7 +100,8 @@ class Invocation(ABC):
|
|
|
94
100
|
|
|
95
101
|
job_response = build_error_job_response(exception,
|
|
96
102
|
message='Error when prepare backend data',
|
|
97
|
-
stage='while preparing the backend'
|
|
103
|
+
stage='while preparing the backend',
|
|
104
|
+
phase=RuntimePhase.DEVICE_SELECTION)
|
|
98
105
|
job_response.authentication = self.authentication
|
|
99
106
|
update_job_metadata(job_response, self.callback_dict.get(
|
|
100
107
|
InvocationStep.PREPARATION).on_error)
|
|
@@ -125,10 +132,15 @@ class Invocation(ABC):
|
|
|
125
132
|
self.logger.debug(
|
|
126
133
|
f"Executing job with provider tag: {provider_tag.value}")
|
|
127
134
|
|
|
128
|
-
|
|
135
|
+
# PROVIDER_INVOCATION tách riêng khỏi EXECUTION: lỗi authenticate
|
|
136
|
+
# với provider và lỗi provider từ chối circuit là hai chuyện khác
|
|
137
|
+
# nhau, trước đây cùng ra nhãn EXECUTION.
|
|
138
|
+
with phase(RuntimePhase.PROVIDER_INVOCATION):
|
|
139
|
+
provider = self._create_provider()
|
|
129
140
|
|
|
130
|
-
|
|
131
|
-
|
|
141
|
+
self.logger.debug(
|
|
142
|
+
f"Executing job with device name: {device_name}")
|
|
143
|
+
device = self._create_device(provider)
|
|
132
144
|
|
|
133
145
|
except Exception as exception:
|
|
134
146
|
self.logger.exception(
|
|
@@ -137,7 +149,8 @@ class Invocation(ABC):
|
|
|
137
149
|
|
|
138
150
|
job_response = build_error_job_response(exception,
|
|
139
151
|
message='Error when create provider or device',
|
|
140
|
-
stage='while running the circuit on the device'
|
|
152
|
+
stage='while running the circuit on the device',
|
|
153
|
+
phase=RuntimePhase.PROVIDER_INVOCATION)
|
|
141
154
|
update_job_metadata(job_response, self.callback_dict.get(
|
|
142
155
|
InvocationStep.EXECUTION).on_error, )
|
|
143
156
|
|
|
@@ -170,7 +183,8 @@ class Invocation(ABC):
|
|
|
170
183
|
InvocationStep.PREPARATION).on_start)
|
|
171
184
|
|
|
172
185
|
try:
|
|
173
|
-
|
|
186
|
+
with phase(RuntimePhase.PREPROCESSING):
|
|
187
|
+
circuit = circuit_preparation_fn(self.input)
|
|
174
188
|
|
|
175
189
|
if circuit is None:
|
|
176
190
|
self.logger.warning(
|
|
@@ -190,7 +204,8 @@ class Invocation(ABC):
|
|
|
190
204
|
|
|
191
205
|
job_response = build_error_job_response(exception, job_response,
|
|
192
206
|
message='Error when prepare circuit',
|
|
193
|
-
stage='while preparing the circuit'
|
|
207
|
+
stage='while preparing the circuit',
|
|
208
|
+
phase=RuntimePhase.PREPROCESSING)
|
|
194
209
|
|
|
195
210
|
update_job_metadata(job_response, self.callback_dict.get(
|
|
196
211
|
InvocationStep.PREPARATION).on_error)
|
{quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/backend/job_fetcher.py
RENAMED
|
@@ -16,6 +16,7 @@ from ...data.response.custom_header import CustomHeader
|
|
|
16
16
|
from ...data.response.job_response import JobResponse
|
|
17
17
|
from ...enum.invocation_step import InvocationStep
|
|
18
18
|
from ...enum.media_type import MediaType
|
|
19
|
+
from ...enum.runtime_phase import RuntimePhase
|
|
19
20
|
from ...enum.status.job_status import JobStatus
|
|
20
21
|
from ...enum.status.status_code import StatusCode
|
|
21
22
|
from ...util.json_parser_utils import parse
|
|
@@ -131,9 +132,12 @@ class JobFetcher(ABC):
|
|
|
131
132
|
f"Exception during job fetch for provider_job_id {self.provider_job_id}: {exception}",
|
|
132
133
|
exc_info=True)
|
|
133
134
|
|
|
134
|
-
job_response = build_error_job_response(
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
job_response = build_error_job_response(
|
|
136
|
+
exception, job_response,
|
|
137
|
+
message='Error when fetching job with provider_job_id {0}'.format(
|
|
138
|
+
self.provider_job_id),
|
|
139
|
+
stage='while fetching the job result from the provider',
|
|
140
|
+
phase=RuntimePhase.POLLING)
|
|
137
141
|
update_job_metadata(job_response, self.callback_urls[
|
|
138
142
|
InvocationStep.EXECUTION].on_error)
|
|
139
143
|
|
|
@@ -208,8 +212,11 @@ class JobFetcher(ABC):
|
|
|
208
212
|
self.logger.exception(f"Exception during analysis: {exception}",
|
|
209
213
|
exc_info=True)
|
|
210
214
|
from ...util.response_utils import build_error_job_response
|
|
211
|
-
job_response = build_error_job_response(
|
|
212
|
-
|
|
215
|
+
job_response = build_error_job_response(
|
|
216
|
+
exception, job_response,
|
|
217
|
+
message='Error when analyzing job result',
|
|
218
|
+
stage='while analysing the job result',
|
|
219
|
+
phase=RuntimePhase.ANALYSIS)
|
|
213
220
|
|
|
214
221
|
update_job_metadata(job_response, callback_url.on_error)
|
|
215
222
|
return None
|
{quapp_common-0.0.13.dev8 → quapp_common-0.0.14.dev1}/quapp_common/component/backend/job_fetching.py
RENAMED
|
@@ -17,6 +17,7 @@ from ...data.response.custom_header import CustomHeader
|
|
|
17
17
|
from ...data.response.job_response import JobResponse
|
|
18
18
|
from ...enum.invocation_step import InvocationStep
|
|
19
19
|
from ...enum.media_type import MediaType
|
|
20
|
+
from ...enum.runtime_phase import RuntimePhase
|
|
20
21
|
from ...enum.status.job_status import JobStatus
|
|
21
22
|
from ...enum.status.status_code import StatusCode
|
|
22
23
|
from ...util.json_parser_utils import parse
|
|
@@ -89,13 +90,16 @@ class JobFetching(ABC):
|
|
|
89
90
|
job_response.status_code = StatusCode.POLLING
|
|
90
91
|
|
|
91
92
|
except Exception as exception:
|
|
92
|
-
self.logger.
|
|
93
|
+
self.logger.bind(event='job_failed').exception(
|
|
93
94
|
"Exception when fetch job with provider_job_id {0}: {1}".format(
|
|
94
95
|
self.provider_job_id, str(exception)))
|
|
95
96
|
|
|
96
|
-
job_response = build_error_job_response(
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
job_response = build_error_job_response(
|
|
98
|
+
exception, job_response,
|
|
99
|
+
message='Exception when fetch job with provider_job_id {0}'.format(
|
|
100
|
+
self.provider_job_id),
|
|
101
|
+
stage='while fetching the job result from the provider',
|
|
102
|
+
phase=RuntimePhase.POLLING)
|
|
99
103
|
|
|
100
104
|
update_job_metadata(job_response, self.callback_dict.get(
|
|
101
105
|
InvocationStep.EXECUTION).on_error)
|
|
@@ -156,9 +160,12 @@ class JobFetching(ABC):
|
|
|
156
160
|
"Exception when analyst job result with provider_job_id {0}: {1}".format(
|
|
157
161
|
self.provider_job_id, str(exception)))
|
|
158
162
|
|
|
159
|
-
job_response = build_error_job_response(
|
|
160
|
-
|
|
161
|
-
|
|
163
|
+
job_response = build_error_job_response(
|
|
164
|
+
exception, job_response,
|
|
165
|
+
message='Exception when analyst job result with provider_job_id {0}'.format(
|
|
166
|
+
self.provider_job_id),
|
|
167
|
+
stage='while analysing the job result',
|
|
168
|
+
phase=RuntimePhase.ANALYSIS)
|
|
162
169
|
|
|
163
170
|
update_job_metadata(job_response, callback_url.on_error)
|
|
164
171
|
|
|
@@ -183,8 +190,10 @@ class JobFetching(ABC):
|
|
|
183
190
|
PostProcessingTask(post_processing_fn=post_processing_fn,
|
|
184
191
|
promise=promise).do()
|
|
185
192
|
|
|
186
|
-
@staticmethod
|
|
187
|
-
|
|
193
|
+
# Trước đây hai method dưới là @staticmethod nhưng vẫn dùng `self.logger`,
|
|
194
|
+
# nên chúng raise NameError ngay khi được gọi -- biến lỗi analysis thành một
|
|
195
|
+
# lỗi vô nghĩa hoàn toàn khác. Bỏ @staticmethod để `self` hợp lệ.
|
|
196
|
+
def __produce_histogram_data(self, job_result) -> Any | None:
|
|
188
197
|
"""
|
|
189
198
|
|
|
190
199
|
@param job_result:
|
|
@@ -196,14 +205,13 @@ class JobFetching(ABC):
|
|
|
196
205
|
histogram_data = job_result.result()[0].data.meas.get_counts()
|
|
197
206
|
|
|
198
207
|
except Exception as e:
|
|
199
|
-
self.logger.
|
|
208
|
+
self.logger.warning(
|
|
200
209
|
"Can't produce histogram with error: {0}".format(str(e)))
|
|
201
210
|
histogram_data = None
|
|
202
211
|
|
|
203
212
|
return histogram_data
|
|
204
213
|
|
|
205
|
-
|
|
206
|
-
def __get_execution_time(job_result):
|
|
214
|
+
def __get_execution_time(self, job_result):
|
|
207
215
|
"""
|
|
208
216
|
|
|
209
217
|
@param job_result:
|