@cartanova/qgrid-cli 2.3.7 → 2.3.9
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.
- package/README.ko.md +131 -0
- package/README.md +60 -44
- package/bundle/dist/application/qgrid/qgrid-image-generation.js +42 -0
- package/bundle/dist/application/qgrid/qgrid-response-format.js +44 -0
- package/bundle/dist/application/qgrid/qgrid-run-lifecycle.js +23 -4
- package/bundle/dist/application/qgrid/qgrid.dispatcher.js +15 -6
- package/bundle/dist/application/qgrid/qgrid.frame.js +32 -4
- package/bundle/dist/application/qgrid/qgrid.types.js +36 -12
- package/bundle/dist/application/qgrid/tool-emulation.js +16 -8
- package/bundle/dist/application/request-log/request-log.model.js +5 -2
- package/bundle/dist/application/sonamu.generated.js +13 -4
- package/bundle/dist/application/sonamu.generated.sso.js +9 -3
- package/bundle/dist/i18n/sd.generated.js +4 -1
- package/bundle/dist/migrations/20260705140614_alter_request_logs_add1_alter1.js +16 -0
- package/bundle/dist/migrations/20260706120000_alter_request_logs_add_image_cost.js +17 -0
- package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +2 -1
- package/bundle/dist/utils/providers/openai/codex-rpc.js +1 -1
- package/bundle/dist/utils/providers/openai/codex-worker.js +78 -6
- package/bundle/dist/utils/providers/openai/openai-dispatcher.js +21 -4
- package/bundle/src/application/qgrid/qgrid-image-generation.ts +62 -0
- package/bundle/src/application/qgrid/qgrid-response-format.ts +59 -0
- package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +105 -0
- package/bundle/src/application/qgrid/qgrid-run-lifecycle.ts +34 -2
- package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +33 -0
- package/bundle/src/application/qgrid/qgrid.dispatcher.ts +16 -5
- package/bundle/src/application/qgrid/qgrid.frame.test.ts +78 -0
- package/bundle/src/application/qgrid/qgrid.frame.ts +48 -5
- package/bundle/src/application/qgrid/qgrid.types.ts +22 -0
- package/bundle/src/application/qgrid/tool-emulation.test.ts +44 -1
- package/bundle/src/application/qgrid/tool-emulation.ts +27 -4
- package/bundle/src/application/request-log/request-log.entity.json +73 -47
- package/bundle/src/application/request-log/request-log.model.ts +7 -0
- package/bundle/src/application/sonamu.generated.http +16 -3
- package/bundle/src/application/sonamu.generated.sso.ts +6 -0
- package/bundle/src/application/sonamu.generated.ts +12 -0
- package/bundle/src/i18n/sd.generated.ts +3 -0
- package/bundle/src/migrations/20260705140614_alter_request_logs_add1_alter1.ts +15 -0
- package/bundle/src/migrations/20260706120000_alter_request_logs_add_image_cost.ts +14 -0
- package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +5 -0
- package/bundle/src/utils/providers/common/provider-dispatcher.ts +14 -0
- package/bundle/src/utils/providers/openai/codex-rpc.ts +2 -0
- package/bundle/src/utils/providers/openai/codex-worker.test.ts +219 -0
- package/bundle/src/utils/providers/openai/codex-worker.ts +154 -5
- package/bundle/src/utils/providers/openai/openai-dispatcher.test.ts +95 -1
- package/bundle/src/utils/providers/openai/openai-dispatcher.ts +42 -0
- package/bundle/web-dist/client/assets/index-Cj1iWi5i.js +78 -0
- package/bundle/web-dist/client/assets/index-VE_24EJf.css +1 -0
- package/bundle/web-dist/client/assets/{logs-B0pWpDrg.js → logs-DvGWhZTu.js} +1 -1
- package/bundle/web-dist/client/assets/{routes-7XhVuG7o.js → routes-B_xvkM-J.js} +1 -1
- package/bundle/web-dist/client/assets/{sd.generated-DXhjjL2f.js → sd.generated-BrUnRt0B.js} +1 -1
- package/bundle/web-dist/client/assets/{services.generated-B7u0U1wC.js → services.generated-AsEvHbJ7.js} +1 -1
- package/bundle/web-dist/client/assets/{show-CcjZ9nr7.js → show-C6W3ywZz.js} +3 -3
- package/bundle/web-dist/client/assets/{tokens-DlwAm0v4.js → tokens-CxjeN72g.js} +1 -1
- package/bundle/web-dist/client/index.html +3 -3
- package/bundle/web-dist/server/assets/{lazyRouteComponent-B0LkbiYf.js → lazyRouteComponent-BswulcOx.js} +1 -1
- package/bundle/web-dist/server/assets/{logs-dLx6gMXT.js → logs-B_ZLzpwY.js} +4 -4
- package/bundle/web-dist/server/assets/{routes-DD3Hb9xO.js → routes-D9OiKoqa.js} +2 -2
- package/bundle/web-dist/server/assets/{sd.generated-DdfpD_iN.js → sd.generated-DRuQSIKo.js} +3 -0
- package/bundle/web-dist/server/assets/{services.generated-D3ZCvIkt.js → services.generated-Dp0uKgpV.js} +1 -1
- package/bundle/web-dist/server/assets/{show-BQjeyv6A.js → show-CGlX9OYt.js} +104 -13
- package/bundle/web-dist/server/assets/{tokens-CSOsBpP7.js → tokens-Cjw6cQxZ.js} +2 -2
- package/bundle/web-dist/server/entry-server.generated.js +9 -8
- package/package.json +7 -3
- package/postinstall.mjs +119 -0
- package/skills/qgrid/SKILL.md +54 -0
- package/skills/qgrid/agents/openai.yaml +4 -0
- package/skills/qgrid/references/ai-sdk-provider-contract.md +100 -0
- package/skills/qgrid/references/anthropic-claude-code-runtime.md +170 -0
- package/skills/qgrid/references/cli-env-and-server-boot.md +66 -0
- package/skills/qgrid/references/decision-rationale.md +179 -0
- package/skills/qgrid/references/docs-workflow.md +32 -0
- package/skills/qgrid/references/openai-codex-runtime.md +153 -0
- package/skills/qgrid/references/prompt-cache-and-usage.md +101 -0
- package/skills/qgrid/references/provider-runtime-differences.md +54 -0
- package/skills/qgrid/references/repo-map.md +37 -0
- package/skills/qgrid/references/request-log-run-lifecycle.md +76 -0
- package/skills/qgrid/references/sonamu-api-web-flow.md +47 -0
- package/skills/qgrid/references/token-auth-quota-lifecycle.md +205 -0
- package/skills/qgrid/references/tool-calling-and-multiturn.md +144 -0
- package/skills/qgrid/references/verification-and-debugging.md +193 -0
- package/bundle/web-dist/client/assets/index-5WdFEqZT.css +0 -1
- package/bundle/web-dist/client/assets/index-jnUY7nJ-.js +0 -78
package/README.ko.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# @cartanova/qgrid-cli
|
|
2
|
+
|
|
3
|
+
[English](./README.md) · **한국어**
|
|
4
|
+
|
|
5
|
+
Qgrid 서버를 한 줄로 실행. OpenAI/Anthropic 구독 크레딧을 HTTP API + 대시보드로 제공하는 LLM 프록시.
|
|
6
|
+
|
|
7
|
+
## 설치
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i -g @cartanova/qgrid-cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
설치 시 qgrid skills가 자동 동기화됨:
|
|
14
|
+
|
|
15
|
+
- global install: `~/.codex/skills/qgrid`, `~/.claude/skills/qgrid`
|
|
16
|
+
- project install: `.agents/skills/qgrid`, `.claude/skills/qgrid`
|
|
17
|
+
|
|
18
|
+
project install에서는 symlink를 만들고, symlink가 실패하면 copy로 fallback함.
|
|
19
|
+
|
|
20
|
+
## PostgreSQL 준비
|
|
21
|
+
|
|
22
|
+
Qgrid는 OAuth 토큰과 request log를 저장하기 위해 PostgreSQL이 필요함.
|
|
23
|
+
이미 접근 가능한 PostgreSQL이 있으면 `--db` 또는 `QGRID_DB_*` 환경변수로 연결하면 됨.
|
|
24
|
+
로컬에 PostgreSQL이 없으면 Docker로 띄울 수 있음:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
docker run --name qgrid-postgres \
|
|
28
|
+
-e POSTGRES_USER=postgres \
|
|
29
|
+
-e POSTGRES_PASSWORD=postgres \
|
|
30
|
+
-e POSTGRES_DB=qgrid \
|
|
31
|
+
-p 5432:5432 \
|
|
32
|
+
-d postgres:18
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 사용법
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# DB URL로 실행
|
|
39
|
+
qgrid --db postgres://user:password@host:port/dbname
|
|
40
|
+
|
|
41
|
+
# 포트 지정
|
|
42
|
+
qgrid --db postgres://... -p 3000
|
|
43
|
+
|
|
44
|
+
# 환경변수로 DB 설정 (플래그 생략 가능)
|
|
45
|
+
export QGRID_DB_HOST=dev.example.com
|
|
46
|
+
export QGRID_DB_PORT=5432
|
|
47
|
+
export QGRID_DB_USER=postgres
|
|
48
|
+
export QGRID_DB_PASSWORD=postgres
|
|
49
|
+
export QGRID_DB_NAME=qgrid
|
|
50
|
+
qgrid
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
서버가 뜨면 `http://localhost:44900`에서 대시보드 접속 → OAuth 로그인으로 토큰 등록.
|
|
54
|
+
`-p, --port`를 지정하면 해당 포트로 접속.
|
|
55
|
+
|
|
56
|
+
서버 포트는 기본값 `44900` 또는 `--port`로만 결정됨. `PORT` 환경변수는 CLI 서버 포트 입력으로 사용하지 않음.
|
|
57
|
+
선택한 포트가 이미 사용 중이면 기존 프로세스를 종료하거나 다른 포트로 폴백하지 않고 에러와 함께 종료.
|
|
58
|
+
|
|
59
|
+
Ctrl+C로 종료.
|
|
60
|
+
|
|
61
|
+
## 옵션
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
qgrid [options]
|
|
65
|
+
|
|
66
|
+
--db <url> PostgreSQL 연결 URL
|
|
67
|
+
-p, --port <port> 서버 포트 (기본: 44900)
|
|
68
|
+
--skip-update 자동 업데이트 확인 생략
|
|
69
|
+
-V, --version 버전 출력
|
|
70
|
+
-h, --help 도움말
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
CLI는 실행 시 npm의 최신 버전을 확인하고 major/minor 버전이 올라간 경우에만 자동 업데이트함.
|
|
74
|
+
patch 버전 차이는 자동 업데이트 대상으로 보지 않음.
|
|
75
|
+
|
|
76
|
+
## 환경변수
|
|
77
|
+
|
|
78
|
+
`--db` 플래그가 없으면 아래 환경변수에서 DB 접속 정보를 읽음:
|
|
79
|
+
|
|
80
|
+
| 변수 | 기본값 |
|
|
81
|
+
|------|--------|
|
|
82
|
+
| `QGRID_DB_HOST` | `localhost` |
|
|
83
|
+
| `QGRID_DB_PORT` | `5432` |
|
|
84
|
+
| `QGRID_DB_USER` | `postgres` |
|
|
85
|
+
| `QGRID_DB_PASSWORD` | `postgres` |
|
|
86
|
+
| `QGRID_DB_NAME` | `qgrid` |
|
|
87
|
+
|
|
88
|
+
서버 동작 관련 환경변수:
|
|
89
|
+
|
|
90
|
+
| 변수 | 설명 | 기본값 |
|
|
91
|
+
|------|------|--------|
|
|
92
|
+
| `QGRID_WORKERS_PER_TOKEN` | OpenAI 토큰당 codex worker 수 | `3` (최대 5) |
|
|
93
|
+
| `QGRID_PUBLIC_BASE_URL` | Anthropic OAuth callback 공개 베이스 URL. 원격 접속 환경에서 설정 | 미설정 시 `http://localhost:<port>/callback` |
|
|
94
|
+
| `QGRID_OPENAI_THREAD_REUSE` | `false`로 설정 시 OpenAI thread reuse(prompt cache) 비활성화 | 활성 |
|
|
95
|
+
|
|
96
|
+
## 사전 요구사항
|
|
97
|
+
|
|
98
|
+
- Node.js >= 20
|
|
99
|
+
- PostgreSQL
|
|
100
|
+
- Docker (로컬 PostgreSQL을 컨테이너로 실행할 경우)
|
|
101
|
+
- [Codex CLI](https://github.com/openai/codex) (OpenAI 모델 사용 시)
|
|
102
|
+
- [Claude Code](https://www.anthropic.com/claude-code) (Anthropic 모델 사용 시)
|
|
103
|
+
|
|
104
|
+
## 동작 방식
|
|
105
|
+
|
|
106
|
+
CLI는 Sonamu 기반 서버를 내장 번들로 포함. 실행 시:
|
|
107
|
+
|
|
108
|
+
1. DB 연결 확인
|
|
109
|
+
2. 서버 시작 (API + 대시보드 웹 UI)
|
|
110
|
+
3. DB의 등록된 토큰 로드. 이후 토큰 등록/변경은 PostgreSQL LISTEN/NOTIFY로 실행 중인 서버에 실시간 반영
|
|
111
|
+
4. **OpenAI 토큰**: 토큰당 persistent codex app-server 프로세스 N개 spawn (기본 3, 최대 5). JSON-RPC로 통신. 요청은 idle worker에 round-robin 라우팅, 전부 busy면 큐 대기 (최대 60초). `sessionKey` 멀티턴 요청은 같은 thread로 재라우팅되어 prompt cache 적중
|
|
112
|
+
5. **Anthropic 토큰**: 요청마다 격리된 claude 프로세스를 fresh spawn (`stream-json` 입출력). 토큰은 least-used 우선으로 선택. OAuth 토큰 자동 refresh
|
|
113
|
+
6. 토큰별 quota threshold(기본 80%)를 넘은 토큰은 라우팅에서 제외 (사용률 조회 실패 시에는 fail-open으로 통과)
|
|
114
|
+
|
|
115
|
+
Qgrid 앱 자체는 Docker에 의존하지 않지만 PostgreSQL은 필요함. 로컬 PostgreSQL이 없으면 Docker로 PostgreSQL을 띄우는 구성이 가장 간단함.
|
|
116
|
+
|
|
117
|
+
## SDK 연동
|
|
118
|
+
|
|
119
|
+
서버를 띄운 뒤 [`@cartanova/qgrid-ai-sdk`](../ai-sdk/README.ko.md)로 호출:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { generateText } from "ai";
|
|
123
|
+
import { qgrid } from "@cartanova/qgrid-ai-sdk";
|
|
124
|
+
|
|
125
|
+
const { text } = await generateText({
|
|
126
|
+
model: qgrid("openai/gpt-5.4-mini"),
|
|
127
|
+
prompt: "서울 날씨 알려줘",
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
자세한 사용법은 [`@cartanova/qgrid-ai-sdk` README](../ai-sdk/README.ko.md) 참조.
|
package/README.md
CHANGED
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
# @cartanova/qgrid-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**English** · [한국어](./README.ko.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Run the Qgrid server in one line. An LLM proxy that exposes OpenAI/Anthropic subscription credits as an HTTP API + dashboard.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
10
|
npm i -g @cartanova/qgrid-cli
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
Installing also syncs the qgrid skills automatically:
|
|
14
|
+
|
|
15
|
+
- global install: `~/.codex/skills/qgrid`, `~/.claude/skills/qgrid`
|
|
16
|
+
- project install: `.agents/skills/qgrid`, `.claude/skills/qgrid`
|
|
17
|
+
|
|
18
|
+
Project installs create symlinks, falling back to copies when symlinking fails.
|
|
12
19
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
## Preparing PostgreSQL
|
|
21
|
+
|
|
22
|
+
Qgrid needs PostgreSQL to store OAuth tokens and request logs.
|
|
23
|
+
If you already have a reachable PostgreSQL, connect with `--db` or the `QGRID_DB_*` environment variables.
|
|
24
|
+
If you don't have one locally, you can spin one up with Docker:
|
|
16
25
|
|
|
17
26
|
```bash
|
|
18
27
|
docker run --name qgrid-postgres \
|
|
@@ -23,16 +32,16 @@ docker run --name qgrid-postgres \
|
|
|
23
32
|
-d postgres:18
|
|
24
33
|
```
|
|
25
34
|
|
|
26
|
-
##
|
|
35
|
+
## Usage
|
|
27
36
|
|
|
28
37
|
```bash
|
|
29
|
-
# DB URL
|
|
38
|
+
# Run with a DB URL
|
|
30
39
|
qgrid --db postgres://user:password@host:port/dbname
|
|
31
40
|
|
|
32
|
-
#
|
|
41
|
+
# Specify a port
|
|
33
42
|
qgrid --db postgres://... -p 3000
|
|
34
43
|
|
|
35
|
-
#
|
|
44
|
+
# Configure the DB with environment variables (flags can be omitted)
|
|
36
45
|
export QGRID_DB_HOST=dev.example.com
|
|
37
46
|
export QGRID_DB_PORT=5432
|
|
38
47
|
export QGRID_DB_USER=postgres
|
|
@@ -41,66 +50,73 @@ export QGRID_DB_NAME=qgrid
|
|
|
41
50
|
qgrid
|
|
42
51
|
```
|
|
43
52
|
|
|
44
|
-
|
|
45
|
-
`-p, --port
|
|
53
|
+
Once the server is up, open the dashboard at `http://localhost:44900` → register tokens through OAuth login.
|
|
54
|
+
With `-p, --port` set, connect on that port instead.
|
|
46
55
|
|
|
47
|
-
|
|
48
|
-
|
|
56
|
+
The server port is determined only by the default `44900` or `--port`. The `PORT` environment variable is not read as CLI server port input.
|
|
57
|
+
If the selected port is already in use, the CLI exits with an error instead of killing the existing process or falling back to another port.
|
|
49
58
|
|
|
50
|
-
Ctrl+C
|
|
59
|
+
Stop with Ctrl+C.
|
|
51
60
|
|
|
52
|
-
##
|
|
61
|
+
## Options
|
|
53
62
|
|
|
54
63
|
```
|
|
55
64
|
qgrid [options]
|
|
56
65
|
|
|
57
|
-
--db <url> PostgreSQL
|
|
58
|
-
-p, --port <port>
|
|
59
|
-
--skip-update
|
|
60
|
-
-V, --version
|
|
61
|
-
-h, --help
|
|
66
|
+
--db <url> PostgreSQL connection URL
|
|
67
|
+
-p, --port <port> Server port (default: 44900)
|
|
68
|
+
--skip-update Skip the automatic update check
|
|
69
|
+
-V, --version Print version
|
|
70
|
+
-h, --help Help
|
|
62
71
|
```
|
|
63
72
|
|
|
64
|
-
CLI
|
|
65
|
-
|
|
73
|
+
On startup the CLI checks the latest version on npm and self-updates only when the major/minor version has increased.
|
|
74
|
+
Patch-level differences are not treated as auto-update targets.
|
|
66
75
|
|
|
67
|
-
##
|
|
76
|
+
## Environment variables
|
|
68
77
|
|
|
69
|
-
`--db`
|
|
78
|
+
Without the `--db` flag, DB connection info is read from:
|
|
70
79
|
|
|
71
|
-
|
|
|
80
|
+
| Variable | Default |
|
|
72
81
|
|------|--------|
|
|
73
82
|
| `QGRID_DB_HOST` | `localhost` |
|
|
74
83
|
| `QGRID_DB_PORT` | `5432` |
|
|
75
84
|
| `QGRID_DB_USER` | `postgres` |
|
|
76
85
|
| `QGRID_DB_PASSWORD` | `postgres` |
|
|
77
86
|
| `QGRID_DB_NAME` | `qgrid` |
|
|
78
|
-
| `QGRID_WORKERS_PER_TOKEN` | `3` (OpenAI 토큰당 codex worker 수, 최대 5) |
|
|
79
87
|
|
|
80
|
-
|
|
88
|
+
Server behavior variables:
|
|
89
|
+
|
|
90
|
+
| Variable | Description | Default |
|
|
91
|
+
|------|------|--------|
|
|
92
|
+
| `QGRID_WORKERS_PER_TOKEN` | codex workers per OpenAI token | `3` (max 5) |
|
|
93
|
+
| `QGRID_PUBLIC_BASE_URL` | Public base URL for the Anthropic OAuth callback. Set it when the server is accessed remotely | `http://localhost:<port>/callback` when unset |
|
|
94
|
+
| `QGRID_OPENAI_THREAD_REUSE` | Set to `false` to disable OpenAI thread reuse (prompt caching) | enabled |
|
|
95
|
+
|
|
96
|
+
## Prerequisites
|
|
81
97
|
|
|
82
98
|
- Node.js >= 20
|
|
83
99
|
- PostgreSQL
|
|
84
|
-
- Docker (
|
|
85
|
-
- [Codex CLI](https://github.com/openai/codex) (OpenAI
|
|
86
|
-
- [Claude Code](https://www.anthropic.com/claude-code) (Anthropic
|
|
100
|
+
- Docker (if running PostgreSQL locally as a container)
|
|
101
|
+
- [Codex CLI](https://github.com/openai/codex) (for OpenAI models)
|
|
102
|
+
- [Claude Code](https://www.anthropic.com/claude-code) (for Anthropic models)
|
|
87
103
|
|
|
88
|
-
##
|
|
104
|
+
## How it works
|
|
89
105
|
|
|
90
|
-
CLI
|
|
106
|
+
The CLI ships the Sonamu-based server as a built-in bundle. On launch:
|
|
91
107
|
|
|
92
|
-
1. DB
|
|
93
|
-
2.
|
|
94
|
-
3. DB
|
|
95
|
-
4. **OpenAI
|
|
96
|
-
5. **Anthropic
|
|
97
|
-
6.
|
|
108
|
+
1. Verify the DB connection
|
|
109
|
+
2. Start the server (API + dashboard web UI)
|
|
110
|
+
3. Load registered tokens from the DB. Later token additions/changes propagate to the running server in real time via PostgreSQL LISTEN/NOTIFY
|
|
111
|
+
4. **OpenAI tokens**: spawn N persistent codex app-server processes per token (default 3, max 5), communicating over JSON-RPC. Requests are routed round-robin across idle workers; when all are busy they queue (up to 60 seconds). Multi-turn requests carrying a `sessionKey` are routed back to the same thread for prompt-cache hits
|
|
112
|
+
5. **Anthropic tokens**: spawn a fresh, isolated claude process per request (`stream-json` in/out). Tokens are selected least-used-first. OAuth tokens are refreshed automatically
|
|
113
|
+
6. Tokens over their quota threshold (default 80%) are excluded from routing (usage-lookup failures fail open)
|
|
98
114
|
|
|
99
|
-
Qgrid
|
|
115
|
+
The Qgrid app itself does not depend on Docker, but PostgreSQL is required. If you don't run PostgreSQL locally, running it in Docker is the simplest setup.
|
|
100
116
|
|
|
101
|
-
## SDK
|
|
117
|
+
## SDK integration
|
|
102
118
|
|
|
103
|
-
|
|
119
|
+
Once the server is running, call it with [`@cartanova/qgrid-ai-sdk`](../ai-sdk/README.md):
|
|
104
120
|
|
|
105
121
|
```typescript
|
|
106
122
|
import { generateText } from "ai";
|
|
@@ -108,8 +124,8 @@ import { qgrid } from "@cartanova/qgrid-ai-sdk";
|
|
|
108
124
|
|
|
109
125
|
const { text } = await generateText({
|
|
110
126
|
model: qgrid("openai/gpt-5.4-mini"),
|
|
111
|
-
prompt: "
|
|
127
|
+
prompt: "What's the weather in Seoul?",
|
|
112
128
|
});
|
|
113
129
|
```
|
|
114
130
|
|
|
115
|
-
|
|
131
|
+
See the [`@cartanova/qgrid-ai-sdk` README](../ai-sdk/README.md) for details.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import "../../_virtual/_rolldown/runtime.js";
|
|
2
|
+
//#region src/application/qgrid/qgrid-image-generation.ts
|
|
3
|
+
const CODEX_IMAGE_GENERATION_MODEL = "gpt-image-2";
|
|
4
|
+
const DEFAULT_IMAGE_GENERATION_QUALITY = "medium";
|
|
5
|
+
const DEFAULT_IMAGE_GENERATION_SIZE = "1536x1024";
|
|
6
|
+
const IMAGE_OUTPUT_COST_MICRO_USD = {
|
|
7
|
+
low: {
|
|
8
|
+
"1024x1024": 6e3,
|
|
9
|
+
"1024x1536": 5e3,
|
|
10
|
+
"1536x1024": 5e3
|
|
11
|
+
},
|
|
12
|
+
medium: {
|
|
13
|
+
"1024x1024": 53e3,
|
|
14
|
+
"1024x1536": 41e3,
|
|
15
|
+
"1536x1024": 41e3
|
|
16
|
+
},
|
|
17
|
+
high: {
|
|
18
|
+
"1024x1024": 211e3,
|
|
19
|
+
"1024x1536": 165e3,
|
|
20
|
+
"1536x1024": 165e3
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
function resolveImageGenerationOptions(options) {
|
|
24
|
+
return {
|
|
25
|
+
quality: options?.quality ?? "medium",
|
|
26
|
+
size: options?.size ?? "1536x1024"
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function imageGenerationCostMethod(options) {
|
|
30
|
+
const resolved = resolveImageGenerationOptions(options);
|
|
31
|
+
return `assumed:${CODEX_IMAGE_GENERATION_MODEL}:${resolved.quality}:${resolved.size}:png`;
|
|
32
|
+
}
|
|
33
|
+
function estimateImageGenerationCostMicroUsd(result, options) {
|
|
34
|
+
const imageCount = result.content.filter((item) => item.type === "image").length;
|
|
35
|
+
if (imageCount === 0) return null;
|
|
36
|
+
const resolved = resolveImageGenerationOptions(options);
|
|
37
|
+
return imageCount * IMAGE_OUTPUT_COST_MICRO_USD[resolved.quality][resolved.size];
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { CODEX_IMAGE_GENERATION_MODEL, DEFAULT_IMAGE_GENERATION_QUALITY, DEFAULT_IMAGE_GENERATION_SIZE, estimateImageGenerationCostMicroUsd, imageGenerationCostMethod, resolveImageGenerationOptions };
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicWdyaWQtaW1hZ2UtZ2VuZXJhdGlvbi5qcyIsIm5hbWVzIjpbXSwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBwbGljYXRpb24vcWdyaWQvcWdyaWQtaW1hZ2UtZ2VuZXJhdGlvbi50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICB0eXBlIEltYWdlR2VuZXJhdGlvbk9wdGlvbnMsXG4gIHR5cGUgSW1hZ2VHZW5lcmF0aW9uUXVhbGl0eSxcbiAgdHlwZSBJbWFnZUdlbmVyYXRpb25TaXplLFxuICB0eXBlIFF1ZXJ5T3V0cHV0LFxufSBmcm9tIFwiLi9xZ3JpZC50eXBlc1wiO1xuXG5leHBvcnQgY29uc3QgQ09ERVhfSU1BR0VfR0VORVJBVElPTl9NT0RFTCA9IFwiZ3B0LWltYWdlLTJcIjtcbmV4cG9ydCBjb25zdCBERUZBVUxUX0lNQUdFX0dFTkVSQVRJT05fUVVBTElUWTogSW1hZ2VHZW5lcmF0aW9uUXVhbGl0eSA9IFwibWVkaXVtXCI7XG5leHBvcnQgY29uc3QgREVGQVVMVF9JTUFHRV9HRU5FUkFUSU9OX1NJWkU6IEltYWdlR2VuZXJhdGlvblNpemUgPSBcIjE1MzZ4MTAyNFwiO1xuXG5leHBvcnQgdHlwZSBSZXNvbHZlZEltYWdlR2VuZXJhdGlvbk9wdGlvbnMgPSB7XG4gIHF1YWxpdHk6IEltYWdlR2VuZXJhdGlvblF1YWxpdHk7XG4gIHNpemU6IEltYWdlR2VuZXJhdGlvblNpemU7XG59O1xuXG4vLyBPcGVuQUkgZ3B0LWltYWdlLTIgaW1hZ2Ugb3V0cHV0IGNvc3QgZXN0aW1hdGVzIGJ5IHF1YWxpdHkvc2l6ZTpcbi8vIGh0dHBzOi8vZGV2ZWxvcGVycy5vcGVuYWkuY29tL2FwaS9kb2NzL2d1aWRlcy9pbWFnZS1nZW5lcmF0aW9uI2NhbGN1bGF0aW5nLWNvc3RzXG5jb25zdCBJTUFHRV9PVVRQVVRfQ09TVF9NSUNST19VU0Q6IFJlY29yZDxcbiAgSW1hZ2VHZW5lcmF0aW9uUXVhbGl0eSxcbiAgUmVjb3JkPEltYWdlR2VuZXJhdGlvblNpemUsIG51bWJlcj5cbj4gPSB7XG4gIGxvdzoge1xuICAgIFwiMTAyNHgxMDI0XCI6IDZfMDAwLFxuICAgIFwiMTAyNHgxNTM2XCI6IDVfMDAwLFxuICAgIFwiMTUzNngxMDI0XCI6IDVfMDAwLFxuICB9LFxuICBtZWRpdW06IHtcbiAgICBcIjEwMjR4MTAyNFwiOiA1M18wMDAsXG4gICAgXCIxMDI0eDE1MzZcIjogNDFfMDAwLFxuICAgIFwiMTUzNngxMDI0XCI6IDQxXzAwMCxcbiAgfSxcbiAgaGlnaDoge1xuICAgIFwiMTAyNHgxMDI0XCI6IDIxMV8wMDAsXG4gICAgXCIxMDI0eDE1MzZcIjogMTY1XzAwMCxcbiAgICBcIjE1MzZ4MTAyNFwiOiAxNjVfMDAwLFxuICB9LFxufTtcblxuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVJbWFnZUdlbmVyYXRpb25PcHRpb25zKFxuICBvcHRpb25zOiBJbWFnZUdlbmVyYXRpb25PcHRpb25zIHwgdW5kZWZpbmVkLFxuKTogUmVzb2x2ZWRJbWFnZUdlbmVyYXRpb25PcHRpb25zIHtcbiAgcmV0dXJuIHtcbiAgICBxdWFsaXR5OiBvcHRpb25zPy5xdWFsaXR5ID8/IERFRkFVTFRfSU1BR0VfR0VORVJBVElPTl9RVUFMSVRZLFxuICAgIHNpemU6IG9wdGlvbnM/LnNpemUgPz8gREVGQVVMVF9JTUFHRV9HRU5FUkFUSU9OX1NJWkUsXG4gIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpbWFnZUdlbmVyYXRpb25Db3N0TWV0aG9kKG9wdGlvbnM6IEltYWdlR2VuZXJhdGlvbk9wdGlvbnMgfCB1bmRlZmluZWQpOiBzdHJpbmcge1xuICBjb25zdCByZXNvbHZlZCA9IHJlc29sdmVJbWFnZUdlbmVyYXRpb25PcHRpb25zKG9wdGlvbnMpO1xuICByZXR1cm4gYGFzc3VtZWQ6JHtDT0RFWF9JTUFHRV9HRU5FUkFUSU9OX01PREVMfToke3Jlc29sdmVkLnF1YWxpdHl9OiR7cmVzb2x2ZWQuc2l6ZX06cG5nYDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGVzdGltYXRlSW1hZ2VHZW5lcmF0aW9uQ29zdE1pY3JvVXNkKFxuICByZXN1bHQ6IFF1ZXJ5T3V0cHV0LFxuICBvcHRpb25zOiBJbWFnZUdlbmVyYXRpb25PcHRpb25zIHwgdW5kZWZpbmVkLFxuKTogbnVtYmVyIHwgbnVsbCB7XG4gIGNvbnN0IGltYWdlQ291bnQgPSByZXN1bHQuY29udGVudC5maWx0ZXIoKGl0ZW0pID0+IGl0ZW0udHlwZSA9PT0gXCJpbWFnZVwiKS5sZW5ndGg7XG4gIGlmIChpbWFnZUNvdW50ID09PSAwKSByZXR1cm4gbnVsbDtcbiAgY29uc3QgcmVzb2x2ZWQgPSByZXNvbHZlSW1hZ2VHZW5lcmF0aW9uT3B0aW9ucyhvcHRpb25zKTtcbiAgcmV0dXJuIGltYWdlQ291bnQgKiBJTUFHRV9PVVRQVVRfQ09TVF9NSUNST19VU0RbcmVzb2x2ZWQucXVhbGl0eV1bcmVzb2x2ZWQuc2l6ZV07XG59XG4iXSwibWFwcGluZ3MiOiI7O0FBT0EsTUFBYSwrQkFBK0I7QUFDNUMsTUFBYSxtQ0FBMkQ7QUFDeEUsTUFBYSxnQ0FBcUQ7QUFTbEUsTUFBTSw4QkFHRjtDQUNGLEtBQUs7RUFDSCxhQUFhO0VBQ2IsYUFBYTtFQUNiLGFBQWE7RUFDZDtDQUNELFFBQVE7RUFDTixhQUFhO0VBQ2IsYUFBYTtFQUNiLGFBQWE7RUFDZDtDQUNELE1BQU07RUFDSixhQUFhO0VBQ2IsYUFBYTtFQUNiLGFBQWE7RUFDZDtDQUNGO0FBRUQsU0FBZ0IsOEJBQ2QsU0FDZ0M7QUFDaEMsUUFBTztFQUNMLFNBQVMsU0FBUyxXQUFBO0VBQ2xCLE1BQU0sU0FBUyxRQUFBO0VBQ2hCOztBQUdILFNBQWdCLDBCQUEwQixTQUFxRDtDQUM3RixNQUFNLFdBQVcsOEJBQThCLFFBQVE7QUFDdkQsUUFBTyxXQUFXLDZCQUE2QixHQUFHLFNBQVMsUUFBUSxHQUFHLFNBQVMsS0FBSzs7QUFHdEYsU0FBZ0Isb0NBQ2QsUUFDQSxTQUNlO0NBQ2YsTUFBTSxhQUFhLE9BQU8sUUFBUSxRQUFRLFNBQVMsS0FBSyxTQUFTLFFBQVEsQ0FBQztBQUMxRSxLQUFJLGVBQWUsRUFBRyxRQUFPO0NBQzdCLE1BQU0sV0FBVyw4QkFBOEIsUUFBUTtBQUN2RCxRQUFPLGFBQWEsNEJBQTRCLFNBQVMsU0FBUyxTQUFTIn0=
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import "../../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { CODEX_IMAGE_GENERATION_MODEL, resolveImageGenerationOptions } from "./qgrid-image-generation.js";
|
|
3
|
+
//#region src/application/qgrid/qgrid-response-format.ts
|
|
4
|
+
const CODEX_IMAGE_GENERATION_TOOL_CONFIG = {
|
|
5
|
+
type: "image_generation",
|
|
6
|
+
outputFormat: "png"
|
|
7
|
+
};
|
|
8
|
+
function escapeHtmlAttribute(value) {
|
|
9
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
10
|
+
}
|
|
11
|
+
function formatResponseForLog(result) {
|
|
12
|
+
return result.content.flatMap((item) => {
|
|
13
|
+
if (item.type === "text") return [item.text];
|
|
14
|
+
if (item.type === "image") {
|
|
15
|
+
const alt = escapeHtmlAttribute(item.revisedPrompt ?? "generated image");
|
|
16
|
+
return [`<img src="data:image/png;base64,${item.data}" alt="${alt}" />`];
|
|
17
|
+
}
|
|
18
|
+
return [];
|
|
19
|
+
}).filter((part) => part.length > 0).join("\n");
|
|
20
|
+
}
|
|
21
|
+
function getImageParts(result) {
|
|
22
|
+
return result.content.filter((item) => item.type === "image");
|
|
23
|
+
}
|
|
24
|
+
function formatImagePartForLog(image) {
|
|
25
|
+
const alt = escapeHtmlAttribute(image.revisedPrompt ?? "generated image");
|
|
26
|
+
return `<img src="data:image/png;base64,${image.data}" alt="${alt}" />`;
|
|
27
|
+
}
|
|
28
|
+
function imageGenerationToolArgs(args) {
|
|
29
|
+
const resolved = resolveImageGenerationOptions(args.imageGenerationOptions);
|
|
30
|
+
return JSON.stringify({
|
|
31
|
+
prompt: args.prompt,
|
|
32
|
+
driverModel: args.model ?? null,
|
|
33
|
+
tool: CODEX_IMAGE_GENERATION_TOOL_CONFIG,
|
|
34
|
+
pricingAssumption: {
|
|
35
|
+
model: CODEX_IMAGE_GENERATION_MODEL,
|
|
36
|
+
quality: resolved.quality,
|
|
37
|
+
size: resolved.size
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { CODEX_IMAGE_GENERATION_TOOL_CONFIG, formatImagePartForLog, formatResponseForLog, getImageParts, imageGenerationToolArgs };
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicWdyaWQtcmVzcG9uc2UtZm9ybWF0LmpzIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9hcHBsaWNhdGlvbi9xZ3JpZC9xZ3JpZC1yZXNwb25zZS1mb3JtYXQudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgdHlwZSBRZ3JpZENvbnRlbnQsXG4gIHR5cGUgUXVlcnlJbnB1dCxcbiAgdHlwZSBRdWVyeU91dHB1dCxcbn0gZnJvbSBcIi4vcWdyaWQudHlwZXNcIjtcbmltcG9ydCB7XG4gIENPREVYX0lNQUdFX0dFTkVSQVRJT05fTU9ERUwsXG4gIHJlc29sdmVJbWFnZUdlbmVyYXRpb25PcHRpb25zLFxufSBmcm9tIFwiLi9xZ3JpZC1pbWFnZS1nZW5lcmF0aW9uXCI7XG5cbmV4cG9ydCBjb25zdCBDT0RFWF9JTUFHRV9HRU5FUkFUSU9OX1RPT0xfQ09ORklHID0ge1xuICB0eXBlOiBcImltYWdlX2dlbmVyYXRpb25cIixcbiAgb3V0cHV0Rm9ybWF0OiBcInBuZ1wiLFxufSBhcyBjb25zdDtcblxuZnVuY3Rpb24gZXNjYXBlSHRtbEF0dHJpYnV0ZSh2YWx1ZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIHZhbHVlXG4gICAgLnJlcGxhY2UoLyYvZywgXCImYW1wO1wiKVxuICAgIC5yZXBsYWNlKC9cIi9nLCBcIiZxdW90O1wiKVxuICAgIC5yZXBsYWNlKC88L2csIFwiJmx0O1wiKVxuICAgIC5yZXBsYWNlKC8+L2csIFwiJmd0O1wiKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdFJlc3BvbnNlRm9yTG9nKHJlc3VsdDogUXVlcnlPdXRwdXQpOiBzdHJpbmcge1xuICByZXR1cm4gcmVzdWx0LmNvbnRlbnRcbiAgICAuZmxhdE1hcCgoaXRlbSkgPT4ge1xuICAgICAgaWYgKGl0ZW0udHlwZSA9PT0gXCJ0ZXh0XCIpIHJldHVybiBbaXRlbS50ZXh0XTtcbiAgICAgIGlmIChpdGVtLnR5cGUgPT09IFwiaW1hZ2VcIikge1xuICAgICAgICBjb25zdCBhbHQgPSBlc2NhcGVIdG1sQXR0cmlidXRlKGl0ZW0ucmV2aXNlZFByb21wdCA/PyBcImdlbmVyYXRlZCBpbWFnZVwiKTtcbiAgICAgICAgcmV0dXJuIFtgPGltZyBzcmM9XCJkYXRhOmltYWdlL3BuZztiYXNlNjQsJHtpdGVtLmRhdGF9XCIgYWx0PVwiJHthbHR9XCIgLz5gXTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBbXTtcbiAgICB9KVxuICAgIC5maWx0ZXIoKHBhcnQpID0+IHBhcnQubGVuZ3RoID4gMClcbiAgICAuam9pbihcIlxcblwiKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEltYWdlUGFydHMocmVzdWx0OiBRdWVyeU91dHB1dCk6IEV4dHJhY3Q8UWdyaWRDb250ZW50LCB7IHR5cGU6IFwiaW1hZ2VcIiB9PltdIHtcbiAgcmV0dXJuIHJlc3VsdC5jb250ZW50LmZpbHRlcigoaXRlbSkgPT4gaXRlbS50eXBlID09PSBcImltYWdlXCIpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZm9ybWF0SW1hZ2VQYXJ0Rm9yTG9nKGltYWdlOiBFeHRyYWN0PFFncmlkQ29udGVudCwgeyB0eXBlOiBcImltYWdlXCIgfT4pOiBzdHJpbmcge1xuICBjb25zdCBhbHQgPSBlc2NhcGVIdG1sQXR0cmlidXRlKGltYWdlLnJldmlzZWRQcm9tcHQgPz8gXCJnZW5lcmF0ZWQgaW1hZ2VcIik7XG4gIHJldHVybiBgPGltZyBzcmM9XCJkYXRhOmltYWdlL3BuZztiYXNlNjQsJHtpbWFnZS5kYXRhfVwiIGFsdD1cIiR7YWx0fVwiIC8+YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGltYWdlR2VuZXJhdGlvblRvb2xBcmdzKGFyZ3M6IFF1ZXJ5SW5wdXQpOiBzdHJpbmcge1xuICBjb25zdCByZXNvbHZlZCA9IHJlc29sdmVJbWFnZUdlbmVyYXRpb25PcHRpb25zKGFyZ3MuaW1hZ2VHZW5lcmF0aW9uT3B0aW9ucyk7XG4gIHJldHVybiBKU09OLnN0cmluZ2lmeSh7XG4gICAgcHJvbXB0OiBhcmdzLnByb21wdCxcbiAgICBkcml2ZXJNb2RlbDogYXJncy5tb2RlbCA/PyBudWxsLFxuICAgIHRvb2w6IENPREVYX0lNQUdFX0dFTkVSQVRJT05fVE9PTF9DT05GSUcsXG4gICAgcHJpY2luZ0Fzc3VtcHRpb246IHtcbiAgICAgIG1vZGVsOiBDT0RFWF9JTUFHRV9HRU5FUkFUSU9OX01PREVMLFxuICAgICAgcXVhbGl0eTogcmVzb2x2ZWQucXVhbGl0eSxcbiAgICAgIHNpemU6IHJlc29sdmVkLnNpemUsXG4gICAgfSxcbiAgfSk7XG59XG4iXSwibWFwcGluZ3MiOiI7OztBQVVBLE1BQWEscUNBQXFDO0NBQ2hELE1BQU07Q0FDTixjQUFjO0NBQ2Y7QUFFRCxTQUFTLG9CQUFvQixPQUF1QjtBQUNsRCxRQUFPLE1BQ0osUUFBUSxNQUFNLFFBQVEsQ0FDdEIsUUFBUSxNQUFNLFNBQVMsQ0FDdkIsUUFBUSxNQUFNLE9BQU8sQ0FDckIsUUFBUSxNQUFNLE9BQU87O0FBRzFCLFNBQWdCLHFCQUFxQixRQUE2QjtBQUNoRSxRQUFPLE9BQU8sUUFDWCxTQUFTLFNBQVM7QUFDakIsTUFBSSxLQUFLLFNBQVMsT0FBUSxRQUFPLENBQUMsS0FBSyxLQUFLO0FBQzVDLE1BQUksS0FBSyxTQUFTLFNBQVM7R0FDekIsTUFBTSxNQUFNLG9CQUFvQixLQUFLLGlCQUFpQixrQkFBa0I7QUFDeEUsVUFBTyxDQUFDLG1DQUFtQyxLQUFLLEtBQUssU0FBUyxJQUFJLE1BQU07O0FBRTFFLFNBQU8sRUFBRTtHQUNULENBQ0QsUUFBUSxTQUFTLEtBQUssU0FBUyxFQUFFLENBQ2pDLEtBQUssS0FBSzs7QUFHZixTQUFnQixjQUFjLFFBQWlFO0FBQzdGLFFBQU8sT0FBTyxRQUFRLFFBQVEsU0FBUyxLQUFLLFNBQVMsUUFBUTs7QUFHL0QsU0FBZ0Isc0JBQXNCLE9BQXlEO0NBQzdGLE1BQU0sTUFBTSxvQkFBb0IsTUFBTSxpQkFBaUIsa0JBQWtCO0FBQ3pFLFFBQU8sbUNBQW1DLE1BQU0sS0FBSyxTQUFTLElBQUk7O0FBR3BFLFNBQWdCLHdCQUF3QixNQUEwQjtDQUNoRSxNQUFNLFdBQVcsOEJBQThCLEtBQUssdUJBQXVCO0FBQzNFLFFBQU8sS0FBSyxVQUFVO0VBQ3BCLFFBQVEsS0FBSztFQUNiLGFBQWEsS0FBSyxTQUFTO0VBQzNCLE1BQU07RUFDTixtQkFBbUI7R0FDakIsT0FBTztHQUNQLFNBQVMsU0FBUztHQUNsQixNQUFNLFNBQVM7R0FDaEI7RUFDRixDQUFDIn0=
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import "../../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { estimateImageGenerationCostMicroUsd, imageGenerationCostMethod } from "./qgrid-image-generation.js";
|
|
3
|
+
import { formatImagePartForLog, formatResponseForLog, getImageParts, imageGenerationToolArgs } from "./qgrid-response-format.js";
|
|
2
4
|
import { RequestLogModel } from "../request-log/request-log.model.js";
|
|
3
5
|
import { getLogger } from "@logtape/logtape";
|
|
4
6
|
//#region src/application/qgrid/qgrid-run-lifecycle.ts
|
|
@@ -39,7 +41,8 @@ async function beforeQuery(args) {
|
|
|
39
41
|
system_prompt: args.system,
|
|
40
42
|
model_name: args.model,
|
|
41
43
|
effort: args.effort,
|
|
42
|
-
project_name: args.projectName
|
|
44
|
+
project_name: args.projectName,
|
|
45
|
+
is_image_generation: args.imageGeneration
|
|
43
46
|
});
|
|
44
47
|
return {
|
|
45
48
|
requestLogId,
|
|
@@ -58,6 +61,20 @@ async function afterQuery(requestLogId, stepIndex, args, result) {
|
|
|
58
61
|
ttft_ms: result.ttftMs,
|
|
59
62
|
finish_reason: result.finishReason
|
|
60
63
|
});
|
|
64
|
+
const imageParts = getImageParts(result);
|
|
65
|
+
const imageCostMicroUsd = estimateImageGenerationCostMicroUsd(result, args.imageGenerationOptions);
|
|
66
|
+
for (let i = 0; i < imageParts.length; i++) {
|
|
67
|
+
const image = imageParts[i];
|
|
68
|
+
await RequestLogModel.appendStep(requestLogId, {
|
|
69
|
+
step_index: stepIndex,
|
|
70
|
+
type: "tool_call",
|
|
71
|
+
tool_call_index: i,
|
|
72
|
+
tool_call_id: `image_generation:${stepIndex}:${i}`,
|
|
73
|
+
tool_name: "image_generation",
|
|
74
|
+
tool_args: imageGenerationToolArgs(args),
|
|
75
|
+
tool_result: formatImagePartForLog(image)
|
|
76
|
+
});
|
|
77
|
+
}
|
|
61
78
|
if (result.finishReason === "tool-calls") {
|
|
62
79
|
const toolCalls = result.content.filter((c) => c.type === "tool-call");
|
|
63
80
|
for (let i = 0; i < toolCalls.length; i++) {
|
|
@@ -66,7 +83,7 @@ async function afterQuery(requestLogId, stepIndex, args, result) {
|
|
|
66
83
|
await RequestLogModel.appendStep(requestLogId, {
|
|
67
84
|
step_index: stepIndex,
|
|
68
85
|
type: "tool_call",
|
|
69
|
-
tool_call_index: i,
|
|
86
|
+
tool_call_index: imageParts.length + i,
|
|
70
87
|
tool_call_id: tc.toolCallId,
|
|
71
88
|
tool_name: tc.toolName,
|
|
72
89
|
tool_args: tc.input
|
|
@@ -77,13 +94,15 @@ async function afterQuery(requestLogId, stepIndex, args, result) {
|
|
|
77
94
|
const agg = await RequestLogModel.aggregateStepUsage(requestLogId);
|
|
78
95
|
await RequestLogModel.finishRun(requestLogId, {
|
|
79
96
|
status: "succeeded",
|
|
80
|
-
response: result
|
|
97
|
+
response: formatResponseForLog(result),
|
|
81
98
|
token_name: result.tokenName,
|
|
82
99
|
input_tokens: agg.input_tokens,
|
|
83
100
|
output_tokens: agg.output_tokens,
|
|
84
101
|
cache_read_tokens: agg.cache_read_tokens,
|
|
85
102
|
cache_creation_tokens: agg.cache_creation_tokens,
|
|
86
103
|
duration_ms: agg.duration_ms,
|
|
104
|
+
image_cost_usd: imageCostMicroUsd,
|
|
105
|
+
image_cost_method: imageCostMicroUsd !== null ? imageGenerationCostMethod(args.imageGenerationOptions) : null,
|
|
87
106
|
history: filterHistoryForStorage(args.history)
|
|
88
107
|
});
|
|
89
108
|
return {};
|
|
@@ -135,4 +154,4 @@ async function cleanupStaleRuns() {
|
|
|
135
154
|
//#endregion
|
|
136
155
|
export { afterQuery, beforeQuery, finishRunAborted, finishRunWithError };
|
|
137
156
|
|
|
138
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicWdyaWQtcnVuLWxpZmVjeWNsZS5qcyIsIm5hbWVzIjpbXSwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBwbGljYXRpb24vcWdyaWQvcWdyaWQtcnVuLWxpZmVjeWNsZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBnZXRMb2dnZXIgfSBmcm9tIFwiQGxvZ3RhcGUvbG9ndGFwZVwiO1xuXG5pbXBvcnQgeyBSZXF1ZXN0TG9nTW9kZWwgfSBmcm9tIFwiLi4vcmVxdWVzdC1sb2cvcmVxdWVzdC1sb2cubW9kZWxcIjtcbmltcG9ydCB7XG4gIHR5cGUgUWdyaWRSdW5Db250ZXh0LFxuICB0eXBlIFFncmlkVG9vbFJlc3VsdElucHV0LFxuICB0eXBlIFF1ZXJ5SW5wdXQsXG4gIHR5cGUgUXVlcnlPdXRwdXQsXG59IGZyb20gXCIuL3FncmlkLnR5cGVzXCI7XG5cbmNvbnN0IGxvZ2dlciA9IGdldExvZ2dlcihbXCJxZ3JpZFwiLCBcInJ1bi1saWZlY3ljbGVcIl0pO1xuY29uc3QgU1RBTEVfUlVOX1RIUkVTSE9MRF9NUyA9IDMwICogNjAgKiAxMDAwO1xuXG4vKipcbiAqIGhpc3RvcnnripQg7JuQ67O4IOq3uOuMgOuhnCDsoIDsnqXtlZjquLDrs7Tri6TripQsIHJ1biDrtoTshJ3sl5Ag7ZWE7JqU7ZWcIHVzZXIvYXNzaXN0YW50IOygleuztOunjCDtlYTthLDrp4HtlbTshJwg7KCA7J6lXG4gKi9cbmZ1bmN0aW9uIGZpbHRlckhpc3RvcnlGb3JTdG9yYWdlKHJhd0hpc3Rvcnk6IHN0cmluZyB8IHVuZGVmaW5lZCk6IHVua25vd24ge1xuICBpZiAoIXJhd0hpc3RvcnkpIHJldHVybiB1bmRlZmluZWQ7XG4gIHRyeSB7XG4gICAgY29uc3QgaXRlbXMgPSBKU09OLnBhcnNlKHJhd0hpc3RvcnkpIGFzIHVua25vd25bXTtcbiAgICBjb25zdCBmaWx0ZXJlZCA9IGl0ZW1zLmZpbHRlcigoaXRlbSkgPT4ge1xuICAgICAgaWYgKCFpdGVtIHx8IHR5cGVvZiBpdGVtICE9PSBcIm9iamVjdFwiKSByZXR1cm4gZmFsc2U7XG4gICAgICBjb25zdCByZWNvcmQgPSBpdGVtIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAgICAgcmV0dXJuIHJlY29yZC50eXBlID09PSBcIm1lc3NhZ2VcIiAmJiAocmVjb3JkLnJvbGUgPT09IFwidXNlclwiIHx8IHJlY29yZC5yb2xlID09PSBcImFzc2lzdGFudFwiKTtcbiAgICB9KTtcbiAgICByZXR1cm4gZmlsdGVyZWQubGVuZ3RoID4gMCA/IGZpbHRlcmVkIDogdW5kZWZpbmVkO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG59XG5cbmV4cG9ydCB0eXBlIFJ1bkxpZmVjeWNsZVJlc3VsdCA9IHtcbiAgcnVuQ29udGV4dD86IFFncmlkUnVuQ29udGV4dDtcbn07XG5cbi8qKlxuICogbG9nTW9kZTpcInJ1blwiIOyLnCBxdWVyeS9zdHJlYW0g7J2R64u1IOyghO2bhOyXkCDtmLjstpwuXG4gKlxuICogMSkgYmVmb3JlUXVlcnk6IHJ1biDsg53shLEv6rOE7IaNICsgdG9vbCByZXN1bHQg7JmE66OMICsgc3RhbGUgY2xlYW51cFxuICogMikgYWZ0ZXJRdWVyeTogZ2VuZXJhdGUgc3RlcCArIHRvb2wtY2FsbCBzdGVwICsgZmluaXNoL2tlZXAtb3BlblxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gYmVmb3JlUXVlcnkoYXJnczogUXVlcnlJbnB1dCk6IFByb21pc2U8e1xuICByZXF1ZXN0TG9nSWQ6IG51bWJlcjtcbiAgc3RlcEluZGV4OiBudW1iZXI7XG59PiB7XG4gIGF3YWl0IGNsZWFudXBTdGFsZVJ1bnMoKTtcblxuICBsZXQgcmVxdWVzdExvZ0lkOiBudW1iZXI7XG4gIGxldCBzdGVwSW5kZXggPSAwO1xuXG4gIC8vIHJlcXVlc3RMb2dJZCDqsIAg7J6I7J2EIOuVjOunjCDquLDsobQgcnVuIOyXsOyepS4gdGhyZWFkQ29vcmQg66eMIOyeiOuKlCBjb252IOuKlCDsg4ggcnVuIOycvOuhnCDsi5zsnpEuXG4gIGlmIChhcmdzLnJ1bkNvbnRleHQ/LnJlcXVlc3RMb2dJZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgcmVxdWVzdExvZ0lkID0gYXJncy5ydW5Db250ZXh0LnJlcXVlc3RMb2dJZDtcblxuICAgIGlmIChhcmdzLnRvb2xSZXN1bHRzICYmIGFyZ3MudG9vbFJlc3VsdHMubGVuZ3RoID4gMCkge1xuICAgICAgYXdhaXQgY29tcGxldGVUb29sUmVzdWx0cyhyZXF1ZXN0TG9nSWQsIGFyZ3MudG9vbFJlc3VsdHMpO1xuICAgIH1cblxuICAgIHN0ZXBJbmRleCA9IGF3YWl0IFJlcXVlc3RMb2dNb2RlbC5nZXROZXh0U3RlcEluZGV4KHJlcXVlc3RMb2dJZCk7XG4gIH0gZWxzZSB7XG4gICAgcmVxdWVzdExvZ0lkID0gYXdhaXQgUmVxdWVzdExvZ01vZGVsLmNyZWF0ZVJ1bih7XG4gICAgICB1c2VyX3Byb21wdDogYXJncy5wcm9tcHQsXG4gICAgICBzeXN0ZW1fcHJvbXB0OiBhcmdzLnN5c3RlbSxcbiAgICAgIG1vZGVsX25hbWU6IGFyZ3MubW9kZWwsXG4gICAgICBlZmZvcnQ6IGFyZ3MuZWZmb3J0LFxuICAgICAgcHJvamVjdF9uYW1lOiBhcmdzLnByb2plY3ROYW1lLFxuICAgIH0pO1xuICB9XG5cbiAgcmV0dXJuIHsgcmVxdWVzdExvZ0lkLCBzdGVwSW5kZXggfTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFmdGVyUXVlcnkoXG4gIHJlcXVlc3RMb2dJZDogbnVtYmVyLFxuICBzdGVwSW5kZXg6IG51bWJlcixcbiAgYXJnczogUXVlcnlJbnB1dCxcbiAgcmVzdWx0OiBRdWVyeU91dHB1dCxcbik6IFByb21pc2U8UnVuTGlmZWN5Y2xlUmVzdWx0PiB7XG4gIC8vIGdlbmVyYXRlIHN0ZXAg6riw66GdXG4gIGF3YWl0IFJlcXVlc3RMb2dNb2RlbC5hcHBlbmRTdGVwKHJlcXVlc3RMb2dJZCwge1xuICAgIHN0ZXBfaW5kZXg6IHN0ZXBJbmRleCxcbiAgICB0eXBlOiBcImdlbmVyYXRlXCIsXG4gICAgaW5wdXRfdG9rZW5zOiByZXN1bHQudXNhZ2UuaW5wdXRfdG9rZW5zLFxuICAgIG91dHB1dF90b2tlbnM6IHJlc3VsdC51c2FnZS5vdXRwdXRfdG9rZW5zLFxuICAgIGNhY2hlX3JlYWRfdG9rZW5zOiByZXN1bHQudXNhZ2UuY2FjaGVfcmVhZF9pbnB1dF90b2tlbnMsXG4gICAgY2FjaGVfY3JlYXRpb25fdG9rZW5zOiByZXN1bHQudXNhZ2UuY2FjaGVfY3JlYXRpb25faW5wdXRfdG9rZW5zLFxuICAgIGR1cmF0aW9uX21zOiByZXN1bHQuZHVyYXRpb25NcyxcbiAgICB0dGZ0X21zOiByZXN1bHQudHRmdE1zLFxuICAgIGZpbmlzaF9yZWFzb246IHJlc3VsdC5maW5pc2hSZWFzb24sXG4gIH0pO1xuXG4gIGlmIChyZXN1bHQuZmluaXNoUmVhc29uID09PSBcInRvb2wtY2FsbHNcIikge1xuICAgIC8vIHRvb2wtY2FsbCBzdGVwIOymieyLnCDquLDroZ0gKHJlc3VsdOuKlCDrgpjspJHsl5AgY29tcGxldGVUb29sQ2FsbOuhnCDssYTsm4ApXG4gICAgY29uc3QgdG9vbENhbGxzID0gcmVzdWx0LmNvbnRlbnQuZmlsdGVyKChjKSA9PiBjLnR5cGUgPT09IFwidG9vbC1jYWxsXCIpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdG9vbENhbGxzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCB0YyA9IHRvb2xDYWxsc1tpXSE7XG4gICAgICBpZiAodGMudHlwZSAhPT0gXCJ0b29sLWNhbGxcIikgY29udGludWU7XG4gICAgICBhd2FpdCBSZXF1ZXN0TG9nTW9kZWwuYXBwZW5kU3RlcChyZXF1ZXN0TG9nSWQsIHtcbiAgICAgICAgc3RlcF9pbmRleDogc3RlcEluZGV4LFxuICAgICAgICB0eXBlOiBcInRvb2xfY2FsbFwiLFxuICAgICAgICB0b29sX2NhbGxfaW5kZXg6IGksXG4gICAgICAgIHRvb2xfY2FsbF9pZDogdGMudG9vbENhbGxJZCxcbiAgICAgICAgdG9vbF9uYW1lOiB0Yy50b29sTmFtZSxcbiAgICAgICAgdG9vbF9hcmdzOiB0Yy5pbnB1dCxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHJldHVybiB7IHJ1bkNvbnRleHQ6IHsgcmVxdWVzdExvZ0lkIH0gfTtcbiAgfVxuXG4gIC8vIGZpbmlzaFJlYXNvbiA9PT0gXCJzdG9wXCIg4oaSIHJ1biDsooXro4wgKOyghOyytCBzdGVwIHVzYWdlIO2VqeyCsClcbiAgY29uc3QgYWdnID0gYXdhaXQgUmVxdWVzdExvZ01vZGVsLmFnZ3JlZ2F0ZVN0ZXBVc2FnZShyZXF1ZXN0TG9nSWQpO1xuICBhd2FpdCBSZXF1ZXN0TG9nTW9kZWwuZmluaXNoUnVuKHJlcXVlc3RMb2dJZCwge1xuICAgIHN0YXR1czogXCJzdWNjZWVkZWRcIixcbiAgICByZXNwb25zZTogcmVzdWx0LnRleHQsXG4gICAgdG9rZW5fbmFtZTogcmVzdWx0LnRva2VuTmFtZSxcbiAgICBpbnB1dF90b2tlbnM6IGFnZy5pbnB1dF90b2tlbnMsXG4gICAgb3V0cHV0X3Rva2VuczogYWdnLm91dHB1dF90b2tlbnMsXG4gICAgY2FjaGVfcmVhZF90b2tlbnM6IGFnZy5jYWNoZV9yZWFkX3Rva2VucyxcbiAgICBjYWNoZV9jcmVhdGlvbl90b2tlbnM6IGFnZy5jYWNoZV9jcmVhdGlvbl90b2tlbnMsXG4gICAgZHVyYXRpb25fbXM6IGFnZy5kdXJhdGlvbl9tcyxcbiAgICBoaXN0b3J5OiBmaWx0ZXJIaXN0b3J5Rm9yU3RvcmFnZShhcmdzLmhpc3RvcnkpLFxuICB9KTtcblxuICByZXR1cm4ge307XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBmaW5pc2hSdW5XaXRoRXJyb3IoXG4gIHJlcXVlc3RMb2dJZDogbnVtYmVyLFxuICBlcnJvck1lc3NhZ2U6IHN0cmluZyxcbiAgYXJncz86IFF1ZXJ5SW5wdXQsXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgdHJ5IHtcbiAgICBhd2FpdCBSZXF1ZXN0TG9nTW9kZWwuZmluaXNoUnVuKHJlcXVlc3RMb2dJZCwge1xuICAgICAgc3RhdHVzOiBcImVycm9yXCIsXG4gICAgICBlcnJvcl9tZXNzYWdlOiBlcnJvck1lc3NhZ2UsXG4gICAgICBoaXN0b3J5OiBhcmdzID8gZmlsdGVySGlzdG9yeUZvclN0b3JhZ2UoYXJncy5oaXN0b3J5KSA6IHVuZGVmaW5lZCxcbiAgICB9KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGxvZ2dlci5lcnJvcihgZmluaXNoUnVuV2l0aEVycm9yIGZhaWxlZDogJHsoZSBhcyBFcnJvcikubWVzc2FnZX1gKTtcbiAgfVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZmluaXNoUnVuQWJvcnRlZChyZXF1ZXN0TG9nSWQ6IG51bWJlciwgYXJncz86IFF1ZXJ5SW5wdXQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgdHJ5IHtcbiAgICBhd2FpdCBSZXF1ZXN0TG9nTW9kZWwuZmluaXNoUnVuKHJlcXVlc3RMb2dJZCwge1xuICAgICAgc3RhdHVzOiBcImFib3J0ZWRcIixcbiAgICAgIGVycm9yX21lc3NhZ2U6IFwiY2xpZW50IGRpc2Nvbm5lY3RlZFwiLFxuICAgICAgaGlzdG9yeTogYXJncyA/IGZpbHRlckhpc3RvcnlGb3JTdG9yYWdlKGFyZ3MuaGlzdG9yeSkgOiB1bmRlZmluZWQsXG4gICAgfSk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBsb2dnZXIuZXJyb3IoYGZpbmlzaFJ1bkFib3J0ZWQgZmFpbGVkOiAkeyhlIGFzIEVycm9yKS5tZXNzYWdlfWApO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGNvbXBsZXRlVG9vbFJlc3VsdHMoXG4gIHJlcXVlc3RMb2dJZDogbnVtYmVyLFxuICB0b29sUmVzdWx0czogUWdyaWRUb29sUmVzdWx0SW5wdXRbXSxcbik6IFByb21pc2U8dm9pZD4ge1xuICBmb3IgKGNvbnN0IHRyIG9mIHRvb2xSZXN1bHRzKSB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IFJlcXVlc3RMb2dNb2RlbC5jb21wbGV0ZVRvb2xDYWxsKHJlcXVlc3RMb2dJZCwgdHIudG9vbENhbGxJZCwge1xuICAgICAgICB0b29sX3Jlc3VsdDogdHIuaXNFcnJvciA/IHVuZGVmaW5lZCA6IHRyLm91dHB1dCxcbiAgICAgICAgZXJyb3I6IHRyLmlzRXJyb3IgPyB0ci5vdXRwdXQgOiB1bmRlZmluZWQsXG4gICAgICB9KTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoYGNvbXBsZXRlVG9vbENhbGwgZmFpbGVkIGZvciAke3RyLnRvb2xDYWxsSWR9OiAkeyhlIGFzIEVycm9yKS5tZXNzYWdlfWApO1xuICAgIH1cbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBjbGVhbnVwU3RhbGVSdW5zKCk6IFByb21pc2U8dm9pZD4ge1xuICB0cnkge1xuICAgIGNvbnN0IHN0YWxlSWRzID0gYXdhaXQgUmVxdWVzdExvZ01vZGVsLmZpbmRTdGFsZVJ1bm5pbmdJZHMoU1RBTEVfUlVOX1RIUkVTSE9MRF9NUyk7XG4gICAgZm9yIChjb25zdCBpZCBvZiBzdGFsZUlkcykge1xuICAgICAgYXdhaXQgUmVxdWVzdExvZ01vZGVsLmZpbmlzaFJ1bihpZCwge1xuICAgICAgICBzdGF0dXM6IFwiZXJyb3JcIixcbiAgICAgICAgZXJyb3JfbWVzc2FnZTogXCJ0b29sLWNhbGwgcnVuOiBubyBmb2xsb3ctdXAgd2l0aGluIDMwIG1pbnV0ZXNcIixcbiAgICAgIH0pO1xuICAgIH1cbiAgICBpZiAoc3RhbGVJZHMubGVuZ3RoID4gMCkge1xuICAgICAgbG9nZ2VyLmluZm8oYGNsZWFuZWQgdXAgJHtzdGFsZUlkcy5sZW5ndGh9IHN0YWxlIHJ1bm5pbmcgcmVxdWVzdCBsb2dzYCk7XG4gICAgfVxuICB9IGNhdGNoIChlKSB7XG4gICAgbG9nZ2VyLmVycm9yKGBzdGFsZSBjbGVhbnVwIGZhaWxlZDogJHsoZSBhcyBFcnJvcikubWVzc2FnZX1gKTtcbiAgfVxufVxuIl0sIm1hcHBpbmdzIjoiOzs7O0FBVUEsTUFBTSxTQUFTLFVBQVUsQ0FBQyxTQUFTLGdCQUFnQixDQUFDO0FBQ3BELE1BQU0seUJBQXlCLE9BQVU7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
|
|
157
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicWdyaWQtcnVuLWxpZmVjeWNsZS5qcyIsIm5hbWVzIjpbXSwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBwbGljYXRpb24vcWdyaWQvcWdyaWQtcnVuLWxpZmVjeWNsZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBnZXRMb2dnZXIgfSBmcm9tIFwiQGxvZ3RhcGUvbG9ndGFwZVwiO1xuXG5pbXBvcnQgeyBSZXF1ZXN0TG9nTW9kZWwgfSBmcm9tIFwiLi4vcmVxdWVzdC1sb2cvcmVxdWVzdC1sb2cubW9kZWxcIjtcbmltcG9ydCB7XG4gIHR5cGUgUWdyaWRSdW5Db250ZXh0LFxuICB0eXBlIFFncmlkVG9vbFJlc3VsdElucHV0LFxuICB0eXBlIFF1ZXJ5SW5wdXQsXG4gIHR5cGUgUXVlcnlPdXRwdXQsXG59IGZyb20gXCIuL3FncmlkLnR5cGVzXCI7XG5pbXBvcnQge1xuICBmb3JtYXRJbWFnZVBhcnRGb3JMb2csXG4gIGZvcm1hdFJlc3BvbnNlRm9yTG9nLFxuICBnZXRJbWFnZVBhcnRzLFxuICBpbWFnZUdlbmVyYXRpb25Ub29sQXJncyxcbn0gZnJvbSBcIi4vcWdyaWQtcmVzcG9uc2UtZm9ybWF0XCI7XG5pbXBvcnQge1xuICBlc3RpbWF0ZUltYWdlR2VuZXJhdGlvbkNvc3RNaWNyb1VzZCxcbiAgaW1hZ2VHZW5lcmF0aW9uQ29zdE1ldGhvZCxcbn0gZnJvbSBcIi4vcWdyaWQtaW1hZ2UtZ2VuZXJhdGlvblwiO1xuXG5jb25zdCBsb2dnZXIgPSBnZXRMb2dnZXIoW1wicWdyaWRcIiwgXCJydW4tbGlmZWN5Y2xlXCJdKTtcbmNvbnN0IFNUQUxFX1JVTl9USFJFU0hPTERfTVMgPSAzMCAqIDYwICogMTAwMDtcblxuLyoqXG4gKiBoaXN0b3J564qUIOybkOuzuCDqt7jrjIDroZwg7KCA7J6l7ZWY6riw67O064uk64qULCBydW4g67aE7ISd7JeQIO2VhOyalO2VnCB1c2VyL2Fzc2lzdGFudCDsoJXrs7Trp4wg7ZWE7YSw66eB7ZW07IScIOyggOyepVxuICovXG5mdW5jdGlvbiBmaWx0ZXJIaXN0b3J5Rm9yU3RvcmFnZShyYXdIaXN0b3J5OiBzdHJpbmcgfCB1bmRlZmluZWQpOiB1bmtub3duIHtcbiAgaWYgKCFyYXdIaXN0b3J5KSByZXR1cm4gdW5kZWZpbmVkO1xuICB0cnkge1xuICAgIGNvbnN0IGl0ZW1zID0gSlNPTi5wYXJzZShyYXdIaXN0b3J5KSBhcyB1bmtub3duW107XG4gICAgY29uc3QgZmlsdGVyZWQgPSBpdGVtcy5maWx0ZXIoKGl0ZW0pID0+IHtcbiAgICAgIGlmICghaXRlbSB8fCB0eXBlb2YgaXRlbSAhPT0gXCJvYmplY3RcIikgcmV0dXJuIGZhbHNlO1xuICAgICAgY29uc3QgcmVjb3JkID0gaXRlbSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICAgIHJldHVybiByZWNvcmQudHlwZSA9PT0gXCJtZXNzYWdlXCIgJiYgKHJlY29yZC5yb2xlID09PSBcInVzZXJcIiB8fCByZWNvcmQucm9sZSA9PT0gXCJhc3Npc3RhbnRcIik7XG4gICAgfSk7XG4gICAgcmV0dXJuIGZpbHRlcmVkLmxlbmd0aCA+IDAgPyBmaWx0ZXJlZCA6IHVuZGVmaW5lZDtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxufVxuXG5leHBvcnQgdHlwZSBSdW5MaWZlY3ljbGVSZXN1bHQgPSB7XG4gIHJ1bkNvbnRleHQ/OiBRZ3JpZFJ1bkNvbnRleHQ7XG59O1xuXG4vKipcbiAqIGxvZ01vZGU6XCJydW5cIiDsi5wgcXVlcnkvc3RyZWFtIOydkeuLtSDsoITtm4Tsl5Ag7Zi47LacLlxuICpcbiAqIDEpIGJlZm9yZVF1ZXJ5OiBydW4g7IOd7ISxL+qzhOyGjSArIHRvb2wgcmVzdWx0IOyZhOujjCArIHN0YWxlIGNsZWFudXBcbiAqIDIpIGFmdGVyUXVlcnk6IGdlbmVyYXRlIHN0ZXAgKyB0b29sLWNhbGwgc3RlcCArIGZpbmlzaC9rZWVwLW9wZW5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGJlZm9yZVF1ZXJ5KGFyZ3M6IFF1ZXJ5SW5wdXQpOiBQcm9taXNlPHtcbiAgcmVxdWVzdExvZ0lkOiBudW1iZXI7XG4gIHN0ZXBJbmRleDogbnVtYmVyO1xufT4ge1xuICBhd2FpdCBjbGVhbnVwU3RhbGVSdW5zKCk7XG5cbiAgbGV0IHJlcXVlc3RMb2dJZDogbnVtYmVyO1xuICBsZXQgc3RlcEluZGV4ID0gMDtcblxuICAvLyByZXF1ZXN0TG9nSWQg6rCAIOyeiOydhCDrlYzrp4wg6riw7KG0IHJ1biDsl7DsnqUuIHRocmVhZENvb3JkIOunjCDsnojripQgY29udiDripQg7IOIIHJ1biDsnLzroZwg7Iuc7J6RLlxuICBpZiAoYXJncy5ydW5Db250ZXh0Py5yZXF1ZXN0TG9nSWQgIT09IHVuZGVmaW5lZCkge1xuICAgIHJlcXVlc3RMb2dJZCA9IGFyZ3MucnVuQ29udGV4dC5yZXF1ZXN0TG9nSWQ7XG5cbiAgICBpZiAoYXJncy50b29sUmVzdWx0cyAmJiBhcmdzLnRvb2xSZXN1bHRzLmxlbmd0aCA+IDApIHtcbiAgICAgIGF3YWl0IGNvbXBsZXRlVG9vbFJlc3VsdHMocmVxdWVzdExvZ0lkLCBhcmdzLnRvb2xSZXN1bHRzKTtcbiAgICB9XG5cbiAgICBzdGVwSW5kZXggPSBhd2FpdCBSZXF1ZXN0TG9nTW9kZWwuZ2V0TmV4dFN0ZXBJbmRleChyZXF1ZXN0TG9nSWQpO1xuICB9IGVsc2Uge1xuICAgIHJlcXVlc3RMb2dJZCA9IGF3YWl0IFJlcXVlc3RMb2dNb2RlbC5jcmVhdGVSdW4oe1xuICAgICAgdXNlcl9wcm9tcHQ6IGFyZ3MucHJvbXB0LFxuICAgICAgc3lzdGVtX3Byb21wdDogYXJncy5zeXN0ZW0sXG4gICAgICBtb2RlbF9uYW1lOiBhcmdzLm1vZGVsLFxuICAgICAgZWZmb3J0OiBhcmdzLmVmZm9ydCxcbiAgICAgIHByb2plY3RfbmFtZTogYXJncy5wcm9qZWN0TmFtZSxcbiAgICAgIGlzX2ltYWdlX2dlbmVyYXRpb246IGFyZ3MuaW1hZ2VHZW5lcmF0aW9uLFxuICAgIH0pO1xuICB9XG5cbiAgcmV0dXJuIHsgcmVxdWVzdExvZ0lkLCBzdGVwSW5kZXggfTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFmdGVyUXVlcnkoXG4gIHJlcXVlc3RMb2dJZDogbnVtYmVyLFxuICBzdGVwSW5kZXg6IG51bWJlcixcbiAgYXJnczogUXVlcnlJbnB1dCxcbiAgcmVzdWx0OiBRdWVyeU91dHB1dCxcbik6IFByb21pc2U8UnVuTGlmZWN5Y2xlUmVzdWx0PiB7XG4gIC8vIGdlbmVyYXRlIHN0ZXAg6riw66GdXG4gIGF3YWl0IFJlcXVlc3RMb2dNb2RlbC5hcHBlbmRTdGVwKHJlcXVlc3RMb2dJZCwge1xuICAgIHN0ZXBfaW5kZXg6IHN0ZXBJbmRleCxcbiAgICB0eXBlOiBcImdlbmVyYXRlXCIsXG4gICAgaW5wdXRfdG9rZW5zOiByZXN1bHQudXNhZ2UuaW5wdXRfdG9rZW5zLFxuICAgIG91dHB1dF90b2tlbnM6IHJlc3VsdC51c2FnZS5vdXRwdXRfdG9rZW5zLFxuICAgIGNhY2hlX3JlYWRfdG9rZW5zOiByZXN1bHQudXNhZ2UuY2FjaGVfcmVhZF9pbnB1dF90b2tlbnMsXG4gICAgY2FjaGVfY3JlYXRpb25fdG9rZW5zOiByZXN1bHQudXNhZ2UuY2FjaGVfY3JlYXRpb25faW5wdXRfdG9rZW5zLFxuICAgIGR1cmF0aW9uX21zOiByZXN1bHQuZHVyYXRpb25NcyxcbiAgICB0dGZ0X21zOiByZXN1bHQudHRmdE1zLFxuICAgIGZpbmlzaF9yZWFzb246IHJlc3VsdC5maW5pc2hSZWFzb24sXG4gIH0pO1xuXG4gIGNvbnN0IGltYWdlUGFydHMgPSBnZXRJbWFnZVBhcnRzKHJlc3VsdCk7XG4gIGNvbnN0IGltYWdlQ29zdE1pY3JvVXNkID0gZXN0aW1hdGVJbWFnZUdlbmVyYXRpb25Db3N0TWljcm9Vc2QoXG4gICAgcmVzdWx0LFxuICAgIGFyZ3MuaW1hZ2VHZW5lcmF0aW9uT3B0aW9ucyxcbiAgKTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBpbWFnZVBhcnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3QgaW1hZ2UgPSBpbWFnZVBhcnRzW2ldITtcbiAgICBhd2FpdCBSZXF1ZXN0TG9nTW9kZWwuYXBwZW5kU3RlcChyZXF1ZXN0TG9nSWQsIHtcbiAgICAgIHN0ZXBfaW5kZXg6IHN0ZXBJbmRleCxcbiAgICAgIHR5cGU6IFwidG9vbF9jYWxsXCIsXG4gICAgICB0b29sX2NhbGxfaW5kZXg6IGksXG4gICAgICB0b29sX2NhbGxfaWQ6IGBpbWFnZV9nZW5lcmF0aW9uOiR7c3RlcEluZGV4fToke2l9YCxcbiAgICAgIHRvb2xfbmFtZTogXCJpbWFnZV9nZW5lcmF0aW9uXCIsXG4gICAgICB0b29sX2FyZ3M6IGltYWdlR2VuZXJhdGlvblRvb2xBcmdzKGFyZ3MpLFxuICAgICAgdG9vbF9yZXN1bHQ6IGZvcm1hdEltYWdlUGFydEZvckxvZyhpbWFnZSksXG4gICAgfSk7XG4gIH1cblxuICBpZiAocmVzdWx0LmZpbmlzaFJlYXNvbiA9PT0gXCJ0b29sLWNhbGxzXCIpIHtcbiAgICAvLyB0b29sLWNhbGwgc3RlcCDsponsi5wg6riw66GdIChyZXN1bHTripQg64KY7KSR7JeQIGNvbXBsZXRlVG9vbENhbGzroZwg7LGE7JuAKVxuICAgIGNvbnN0IHRvb2xDYWxscyA9IHJlc3VsdC5jb250ZW50LmZpbHRlcigoYykgPT4gYy50eXBlID09PSBcInRvb2wtY2FsbFwiKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRvb2xDYWxscy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgdGMgPSB0b29sQ2FsbHNbaV0hO1xuICAgICAgaWYgKHRjLnR5cGUgIT09IFwidG9vbC1jYWxsXCIpIGNvbnRpbnVlO1xuICAgICAgYXdhaXQgUmVxdWVzdExvZ01vZGVsLmFwcGVuZFN0ZXAocmVxdWVzdExvZ0lkLCB7XG4gICAgICAgIHN0ZXBfaW5kZXg6IHN0ZXBJbmRleCxcbiAgICAgICAgdHlwZTogXCJ0b29sX2NhbGxcIixcbiAgICAgICAgdG9vbF9jYWxsX2luZGV4OiBpbWFnZVBhcnRzLmxlbmd0aCArIGksXG4gICAgICAgIHRvb2xfY2FsbF9pZDogdGMudG9vbENhbGxJZCxcbiAgICAgICAgdG9vbF9uYW1lOiB0Yy50b29sTmFtZSxcbiAgICAgICAgdG9vbF9hcmdzOiB0Yy5pbnB1dCxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHJldHVybiB7IHJ1bkNvbnRleHQ6IHsgcmVxdWVzdExvZ0lkIH0gfTtcbiAgfVxuXG4gIC8vIGZpbmlzaFJlYXNvbiA9PT0gXCJzdG9wXCIg4oaSIHJ1biDsooXro4wgKOyghOyytCBzdGVwIHVzYWdlIO2VqeyCsClcbiAgY29uc3QgYWdnID0gYXdhaXQgUmVxdWVzdExvZ01vZGVsLmFnZ3JlZ2F0ZVN0ZXBVc2FnZShyZXF1ZXN0TG9nSWQpO1xuICBhd2FpdCBSZXF1ZXN0TG9nTW9kZWwuZmluaXNoUnVuKHJlcXVlc3RMb2dJZCwge1xuICAgIHN0YXR1czogXCJzdWNjZWVkZWRcIixcbiAgICByZXNwb25zZTogZm9ybWF0UmVzcG9uc2VGb3JMb2cocmVzdWx0KSxcbiAgICB0b2tlbl9uYW1lOiByZXN1bHQudG9rZW5OYW1lLFxuICAgIGlucHV0X3Rva2VuczogYWdnLmlucHV0X3Rva2VucyxcbiAgICBvdXRwdXRfdG9rZW5zOiBhZ2cub3V0cHV0X3Rva2VucyxcbiAgICBjYWNoZV9yZWFkX3Rva2VuczogYWdnLmNhY2hlX3JlYWRfdG9rZW5zLFxuICAgIGNhY2hlX2NyZWF0aW9uX3Rva2VuczogYWdnLmNhY2hlX2NyZWF0aW9uX3Rva2VucyxcbiAgICBkdXJhdGlvbl9tczogYWdnLmR1cmF0aW9uX21zLFxuICAgIGltYWdlX2Nvc3RfdXNkOiBpbWFnZUNvc3RNaWNyb1VzZCxcbiAgICBpbWFnZV9jb3N0X21ldGhvZDpcbiAgICAgIGltYWdlQ29zdE1pY3JvVXNkICE9PSBudWxsID8gaW1hZ2VHZW5lcmF0aW9uQ29zdE1ldGhvZChhcmdzLmltYWdlR2VuZXJhdGlvbk9wdGlvbnMpIDogbnVsbCxcbiAgICBoaXN0b3J5OiBmaWx0ZXJIaXN0b3J5Rm9yU3RvcmFnZShhcmdzLmhpc3RvcnkpLFxuICB9KTtcblxuICByZXR1cm4ge307XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBmaW5pc2hSdW5XaXRoRXJyb3IoXG4gIHJlcXVlc3RMb2dJZDogbnVtYmVyLFxuICBlcnJvck1lc3NhZ2U6IHN0cmluZyxcbiAgYXJncz86IFF1ZXJ5SW5wdXQsXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgdHJ5IHtcbiAgICBhd2FpdCBSZXF1ZXN0TG9nTW9kZWwuZmluaXNoUnVuKHJlcXVlc3RMb2dJZCwge1xuICAgICAgc3RhdHVzOiBcImVycm9yXCIsXG4gICAgICBlcnJvcl9tZXNzYWdlOiBlcnJvck1lc3NhZ2UsXG4gICAgICBoaXN0b3J5OiBhcmdzID8gZmlsdGVySGlzdG9yeUZvclN0b3JhZ2UoYXJncy5oaXN0b3J5KSA6IHVuZGVmaW5lZCxcbiAgICB9KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGxvZ2dlci5lcnJvcihgZmluaXNoUnVuV2l0aEVycm9yIGZhaWxlZDogJHsoZSBhcyBFcnJvcikubWVzc2FnZX1gKTtcbiAgfVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZmluaXNoUnVuQWJvcnRlZChyZXF1ZXN0TG9nSWQ6IG51bWJlciwgYXJncz86IFF1ZXJ5SW5wdXQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgdHJ5IHtcbiAgICBhd2FpdCBSZXF1ZXN0TG9nTW9kZWwuZmluaXNoUnVuKHJlcXVlc3RMb2dJZCwge1xuICAgICAgc3RhdHVzOiBcImFib3J0ZWRcIixcbiAgICAgIGVycm9yX21lc3NhZ2U6IFwiY2xpZW50IGRpc2Nvbm5lY3RlZFwiLFxuICAgICAgaGlzdG9yeTogYXJncyA/IGZpbHRlckhpc3RvcnlGb3JTdG9yYWdlKGFyZ3MuaGlzdG9yeSkgOiB1bmRlZmluZWQsXG4gICAgfSk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBsb2dnZXIuZXJyb3IoYGZpbmlzaFJ1bkFib3J0ZWQgZmFpbGVkOiAkeyhlIGFzIEVycm9yKS5tZXNzYWdlfWApO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGNvbXBsZXRlVG9vbFJlc3VsdHMoXG4gIHJlcXVlc3RMb2dJZDogbnVtYmVyLFxuICB0b29sUmVzdWx0czogUWdyaWRUb29sUmVzdWx0SW5wdXRbXSxcbik6IFByb21pc2U8dm9pZD4ge1xuICBmb3IgKGNvbnN0IHRyIG9mIHRvb2xSZXN1bHRzKSB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IFJlcXVlc3RMb2dNb2RlbC5jb21wbGV0ZVRvb2xDYWxsKHJlcXVlc3RMb2dJZCwgdHIudG9vbENhbGxJZCwge1xuICAgICAgICB0b29sX3Jlc3VsdDogdHIuaXNFcnJvciA/IHVuZGVmaW5lZCA6IHRyLm91dHB1dCxcbiAgICAgICAgZXJyb3I6IHRyLmlzRXJyb3IgPyB0ci5vdXRwdXQgOiB1bmRlZmluZWQsXG4gICAgICB9KTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoYGNvbXBsZXRlVG9vbENhbGwgZmFpbGVkIGZvciAke3RyLnRvb2xDYWxsSWR9OiAkeyhlIGFzIEVycm9yKS5tZXNzYWdlfWApO1xuICAgIH1cbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBjbGVhbnVwU3RhbGVSdW5zKCk6IFByb21pc2U8dm9pZD4ge1xuICB0cnkge1xuICAgIGNvbnN0IHN0YWxlSWRzID0gYXdhaXQgUmVxdWVzdExvZ01vZGVsLmZpbmRTdGFsZVJ1bm5pbmdJZHMoU1RBTEVfUlVOX1RIUkVTSE9MRF9NUyk7XG4gICAgZm9yIChjb25zdCBpZCBvZiBzdGFsZUlkcykge1xuICAgICAgYXdhaXQgUmVxdWVzdExvZ01vZGVsLmZpbmlzaFJ1bihpZCwge1xuICAgICAgICBzdGF0dXM6IFwiZXJyb3JcIixcbiAgICAgICAgZXJyb3JfbWVzc2FnZTogXCJ0b29sLWNhbGwgcnVuOiBubyBmb2xsb3ctdXAgd2l0aGluIDMwIG1pbnV0ZXNcIixcbiAgICAgIH0pO1xuICAgIH1cbiAgICBpZiAoc3RhbGVJZHMubGVuZ3RoID4gMCkge1xuICAgICAgbG9nZ2VyLmluZm8oYGNsZWFuZWQgdXAgJHtzdGFsZUlkcy5sZW5ndGh9IHN0YWxlIHJ1bm5pbmcgcmVxdWVzdCBsb2dzYCk7XG4gICAgfVxuICB9IGNhdGNoIChlKSB7XG4gICAgbG9nZ2VyLmVycm9yKGBzdGFsZSBjbGVhbnVwIGZhaWxlZDogJHsoZSBhcyBFcnJvcikubWVzc2FnZX1gKTtcbiAgfVxufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7QUFvQkEsTUFBTSxTQUFTLFVBQVUsQ0FBQyxTQUFTLGdCQUFnQixDQUFDO0FBQ3BELE1BQU0seUJBQXlCLE9BQVU7Ozs7QUFLekMsU0FBUyx3QkFBd0IsWUFBeUM7QUFDeEUsS0FBSSxDQUFDLFdBQVksUUFBTztBQUN4QixLQUFJO0VBRUYsTUFBTSxXQURRLEtBQUssTUFBTSxXQUFXLENBQ2IsUUFBUSxTQUFTO0FBQ3RDLE9BQUksQ0FBQyxRQUFRLE9BQU8sU0FBUyxTQUFVLFFBQU87R0FDOUMsTUFBTSxTQUFTO0FBQ2YsVUFBTyxPQUFPLFNBQVMsY0FBYyxPQUFPLFNBQVMsVUFBVSxPQUFPLFNBQVM7SUFDL0U7QUFDRixTQUFPLFNBQVMsU0FBUyxJQUFJLFdBQVc7U0FDbEM7QUFDTjs7Ozs7Ozs7O0FBY0osZUFBc0IsWUFBWSxNQUcvQjtBQUNELE9BQU0sa0JBQWtCO0NBRXhCLElBQUk7Q0FDSixJQUFJLFlBQVk7QUFHaEIsS0FBSSxLQUFLLFlBQVksaUJBQWlCLFFBQVc7QUFDL0MsaUJBQWUsS0FBSyxXQUFXO0FBRS9CLE1BQUksS0FBSyxlQUFlLEtBQUssWUFBWSxTQUFTLEVBQ2hELE9BQU0sb0JBQW9CLGNBQWMsS0FBSyxZQUFZO0FBRzNELGNBQVksTUFBTSxnQkFBZ0IsaUJBQWlCLGFBQWE7T0FFaEUsZ0JBQWUsTUFBTSxnQkFBZ0IsVUFBVTtFQUM3QyxhQUFhLEtBQUs7RUFDbEIsZUFBZSxLQUFLO0VBQ3BCLFlBQVksS0FBSztFQUNqQixRQUFRLEtBQUs7RUFDYixjQUFjLEtBQUs7RUFDbkIscUJBQXFCLEtBQUs7RUFDM0IsQ0FBQztBQUdKLFFBQU87RUFBRTtFQUFjO0VBQVc7O0FBR3BDLGVBQXNCLFdBQ3BCLGNBQ0EsV0FD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
|