@cartanova/qgrid-ai-sdk 2.15.4 → 2.16.0
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 +2 -1
- package/README.md +2 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +16 -4
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -394,7 +394,7 @@ Grok language model은 user 이미지 part(https URL 또는 inline bytes)를 버
|
|
|
394
394
|
| Image | `n`(호출당 최대 3), `aspectRatio`, `xai.resolution`(`1k`/`1.5k`/`2k`), `xai.quality`, `xai.aspect_ratio`(`aspectRatio`가 없을 때) | 참조 `images`는 `referenceImages`(edit endpoint), `xai.output_format`은 `outputFormat`이 되며 `png`, `jpeg`, `webp`만 허용 | `size`, `seed`, `mask`, `xai.sync_mode`, `xai.user` |
|
|
395
395
|
| Video | `duration`(정수 1-15), `aspectRatio`, `xai.resolution`(`480p`/`720p`/`1080p`), `xai.mode: "reference-to-video"`(지원하는 유일한 mode, `xai.referenceImageUrls` 필요), `xai.pollIntervalMs`, `xai.pollTimeoutMs` | `resolution` `WxH`는 `@ai-sdk/xai`와 같이 매핑(`1280x720` → `720p`), prompt `image`는 시작 프레임, `xai.referenceImageUrls`(7개가 아닌 최대 5개)는 `referenceImages`, `xai.lastFrame`(qgrid 확장, `grok-imagine-video-1.5` 전용) | `fps`, `seed`, `xai.keyframes`, `xai.storageOptions`, `xai.referenceVoiceIds`, `xai.user`. `xai.mode` `edit-video`/`extend-video`와 `xai.videoUrl`은 예외 |
|
|
396
396
|
| Transcription | `xai.language` | 오디오 bytes는 base64로, `mediaType`은 파라미터 제거(`audio/webm;codecs=opus` → `audio/webm`) | 나머지 모든 `xai` 키(`audioFormat`, `sampleRate`, `format`, `diarize`, `keyterm`, ...) |
|
|
397
|
-
| Speech | `voice`, `language` | `outputFormat`은 `format`(`mp3`, `wav`, `pcm`, `mulaw`, `alaw`, 그 외 값은 `@ai-sdk/xai`처럼 warning과 함께 `mp3`) | `instructions`, `speed`, 모든 `xai` 키(`sampleRate`, `bitRate`, `
|
|
397
|
+
| Speech | `voice`, `language`, `xai.withTimestamps` | `outputFormat`은 `format`(`mp3`, `wav`, `pcm`, `mulaw`, `alaw`, 그 외 값은 `@ai-sdk/xai`처럼 warning과 함께 `mp3`) | `instructions`, `speed`, 그 밖의 모든 `xai` 키(`sampleRate`, `bitRate`, `replace`, ...) |
|
|
398
398
|
|
|
399
399
|
**요청 크기.** qgrid 서버는 최대 10 MiB JSON body를 받습니다. inline 미디어는 base64로 전송되어 3분의 1만큼
|
|
400
400
|
커지므로, 서버는 inline 오디오, 각 inline 이미지, 한 요청의 inline 이미지 합계(비디오: `image` + `lastFrame` +
|
|
@@ -487,6 +487,7 @@ speech.audio.uint8Array;
|
|
|
487
487
|
|
|
488
488
|
- `transcribe`는 bytes로 오디오 형식을 감지하고 실패하면 `audio/wav`를 씁니다. 서버는 `audio/*`가 아닌 값을 거부합니다. transcript에는 segment가 없습니다(`segments: []`).
|
|
489
489
|
- 음성 합성 텍스트는 최대 15,000자입니다. `voice`, `language`, `outputFormat`을 지정하지 않으면 서버 기본값 `ara`, `en`, `mp3`가 쓰이며, live 검증된 조합은 이것뿐입니다. 다른 voice, language(`@ai-sdk/xai`의 `auto` 포함), codec은 그대로 전달되지만 미검증입니다. 서버가 돌려준 오디오 MIME 타입은 `providerMetadata.qgrid.mediaType`에 있고, AI SDK의 `speech.audio.mediaType`은 bytes에서 추정한 값입니다.
|
|
490
|
+
- `providerOptions: { xai: { withTimestamps: true } }`(2.16.0+, Grok 전용)를 주면 글자별 타이밍이 `providerMetadata.qgrid.audioTimestamps`에 담깁니다. `graphChars`는 입력 글자(공백·문장부호·음성 태그 포함), `graphTimes`는 같은 인덱스의 `[start, end]` 초이고, 전체 길이는 `providerMetadata.qgrid.duration`입니다. `@ai-sdk/xai`가 `providerMetadata.xai`에 담는 모양과 같습니다. 텍스트 정규화로 풀린 토큰은 첫 글자가 전체 구간을 가집니다. `qgrid.speech("openai/...")`는 타이밍 데이터가 없어 서버가 `withTimestamps`를 HTTP 400으로 거부합니다.
|
|
490
491
|
|
|
491
492
|
### OpenAI 음성 인식과 합성 (ChatGPT 구독)
|
|
492
493
|
|
package/README.md
CHANGED
|
@@ -404,7 +404,7 @@ Grok language models keep user image parts (https URLs or inline bytes) instead
|
|
|
404
404
|
| Image | `n` (up to 3 per call), `aspectRatio`, `xai.resolution` (`1k`/`1.5k`/`2k`), `xai.quality`, `xai.aspect_ratio` (used when `aspectRatio` is unset) | reference `images` become `referenceImages` (edit endpoint); `xai.output_format` becomes `outputFormat` and must be `png`, `jpeg`, or `webp` | `size`, `seed`, `mask`, `xai.sync_mode`, `xai.user` |
|
|
405
405
|
| Video | `duration` (integer 1-15), `aspectRatio`, `xai.resolution` (`480p`/`720p`/`1080p`), `xai.mode: "reference-to-video"` (the only supported mode; needs `xai.referenceImageUrls`), `xai.pollIntervalMs`, `xai.pollTimeoutMs` | `resolution` `WxH` is mapped as in `@ai-sdk/xai` (`1280x720` to `720p`); prompt `image` becomes the start frame; `xai.referenceImageUrls` (up to 5, not 7) become `referenceImages`; `xai.lastFrame` (qgrid extension, `grok-imagine-video-1.5` only) | `fps`, `seed`, `xai.keyframes`, `xai.storageOptions`, `xai.referenceVoiceIds`, `xai.user`. `xai.mode` `edit-video`/`extend-video` and `xai.videoUrl` throw |
|
|
406
406
|
| Transcription | `xai.language` | audio bytes become base64 and `mediaType` loses its parameters (`audio/webm;codecs=opus` to `audio/webm`) | every other `xai` key (`audioFormat`, `sampleRate`, `format`, `diarize`, `keyterm`, ...) |
|
|
407
|
-
| Speech | `voice`, `language` | `outputFormat` becomes `format` (`mp3`, `wav`, `pcm`, `mulaw`, `alaw`; anything else falls back to `mp3` with a warning, as in `@ai-sdk/xai`) | `instructions`, `speed`, every `xai` key (`sampleRate`, `bitRate`, `
|
|
407
|
+
| Speech | `voice`, `language`, `xai.withTimestamps` | `outputFormat` becomes `format` (`mp3`, `wav`, `pcm`, `mulaw`, `alaw`; anything else falls back to `mp3` with a warning, as in `@ai-sdk/xai`) | `instructions`, `speed`, every other `xai` key (`sampleRate`, `bitRate`, `replace`, ...) |
|
|
408
408
|
|
|
409
409
|
**Request size.** The qgrid server accepts JSON bodies up to 10 MiB. Inline media travels as
|
|
410
410
|
base64, which grows by a third, so the server caps inline audio, each inline image, and all
|
|
@@ -499,6 +499,7 @@ speech.audio.uint8Array;
|
|
|
499
499
|
|
|
500
500
|
- `transcribe` detects the audio type from its bytes and falls back to `audio/wav`; the server rejects anything that is not `audio/*`. Transcripts carry no segments (`segments: []`).
|
|
501
501
|
- Speech text is limited to 15,000 characters. Unset `voice`, `language` and `outputFormat` use the server defaults `ara`, `en` and `mp3`, the only combination verified live. Other voices, languages (including `@ai-sdk/xai`'s `auto`) and codecs are passed through but unverified. The server's audio MIME type is in `providerMetadata.qgrid.mediaType`; AI SDK's `speech.audio.mediaType` is sniffed from the bytes.
|
|
502
|
+
- `providerOptions: { xai: { withTimestamps: true } }` (2.16.0+, Grok only) returns per-character timing in `providerMetadata.qgrid.audioTimestamps`: `graphChars` (input characters, spaces, punctuation and speech tags included) and the index-aligned `graphTimes` `[start, end]` seconds, plus `providerMetadata.qgrid.duration`. The shape matches what `@ai-sdk/xai` puts under `providerMetadata.xai`. Text normalization gives an expanded token's whole span to its first character. `qgrid.speech("openai/...")` has no timing data and the server rejects `withTimestamps` with HTTP 400.
|
|
502
503
|
|
|
503
504
|
### OpenAI transcription and speech (ChatGPT subscription)
|
|
504
505
|
|
package/dist/index.d.ts
CHANGED
|
@@ -282,7 +282,9 @@ type QgridOpenAISpeechProviderOptions = {
|
|
|
282
282
|
/**
|
|
283
283
|
* Grok text-to-speech through `POST /api/qgrid/speak`. `voice`, `language` and `outputFormat`
|
|
284
284
|
* map to the server's `voice`, `language` and `format`; when omitted the server applies
|
|
285
|
-
* `ara` / `en` / `mp3
|
|
285
|
+
* `ara` / `en` / `mp3`. The model id only labels the log. `providerOptions.xai.withTimestamps`
|
|
286
|
+
* returns per-character timing in `providerMetadata.qgrid.audioTimestamps` (`graphChars`,
|
|
287
|
+
* `graphTimes`), the shape `@ai-sdk/xai` puts under `providerMetadata.xai`.
|
|
286
288
|
*/
|
|
287
289
|
declare function createQgridSpeech(modelId: QgridSpeechModel, config?: QgridMediaProviderConfig): SpeechModelV3;
|
|
288
290
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -1478,7 +1478,7 @@ function isSpeechFormat(value) {
|
|
|
1478
1478
|
}
|
|
1479
1479
|
/**
|
|
1480
1480
|
* `providerOptions.xai` for Grok speech, field-for-field from `@ai-sdk/xai`
|
|
1481
|
-
* `xaiSpeechModelOptionsSchema`.
|
|
1481
|
+
* `xaiSpeechModelOptionsSchema`. Only `withTimestamps` is honored; every other set key warns.
|
|
1482
1482
|
*/
|
|
1483
1483
|
const xaiSpeechOptionsSchema = z.object({
|
|
1484
1484
|
sampleRate: z.union([
|
|
@@ -1520,11 +1520,18 @@ const OPENAI_SPEECH_FORMATS = ["wav", "pcm"];
|
|
|
1520
1520
|
function isOpenAISpeechFormat(value) {
|
|
1521
1521
|
return OPENAI_SPEECH_FORMATS.some((format) => format === value);
|
|
1522
1522
|
}
|
|
1523
|
+
/** `providerOptions.xai` keys honored by `qgrid.speech("grok/...")`. */
|
|
1524
|
+
const XAI_SPEECH_HONORED = /* @__PURE__ */ new Set(["withTimestamps"]);
|
|
1523
1525
|
const speakOutputSchema = z.object({
|
|
1524
1526
|
audio: z.string(),
|
|
1525
1527
|
mimeType: z.string(),
|
|
1526
1528
|
tokenName: z.string(),
|
|
1527
|
-
requestLogId: z.number().optional()
|
|
1529
|
+
requestLogId: z.number().optional(),
|
|
1530
|
+
audioTimestamps: z.object({
|
|
1531
|
+
graphChars: z.array(z.string()),
|
|
1532
|
+
graphTimes: z.array(z.tuple([z.number(), z.number()]))
|
|
1533
|
+
}).optional(),
|
|
1534
|
+
durationSec: z.number().optional()
|
|
1528
1535
|
});
|
|
1529
1536
|
function base64ToBytes(data) {
|
|
1530
1537
|
if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(data, "base64"));
|
|
@@ -1536,7 +1543,9 @@ function base64ToBytes(data) {
|
|
|
1536
1543
|
/**
|
|
1537
1544
|
* Grok text-to-speech through `POST /api/qgrid/speak`. `voice`, `language` and `outputFormat`
|
|
1538
1545
|
* map to the server's `voice`, `language` and `format`; when omitted the server applies
|
|
1539
|
-
* `ara` / `en` / `mp3
|
|
1546
|
+
* `ara` / `en` / `mp3`. The model id only labels the log. `providerOptions.xai.withTimestamps`
|
|
1547
|
+
* returns per-character timing in `providerMetadata.qgrid.audioTimestamps` (`graphChars`,
|
|
1548
|
+
* `graphTimes`), the shape `@ai-sdk/xai` puts under `providerMetadata.xai`.
|
|
1540
1549
|
*/
|
|
1541
1550
|
function createQgridSpeech(modelId, config) {
|
|
1542
1551
|
const provider = voiceProviderOf(modelId, LABEL$2);
|
|
@@ -1550,7 +1559,7 @@ function createQgridSpeech(modelId, config) {
|
|
|
1550
1559
|
if (!options.text.trim()) throw new Error(`${LABEL$2}: text must not be empty`);
|
|
1551
1560
|
if (options.text.length > MAX_TEXT_CHARS) throw new Error(`${LABEL$2}: text is limited to ${MAX_TEXT_CHARS} characters`);
|
|
1552
1561
|
const qgridOptions = parseGrokQgridOptions(options.providerOptions?.qgrid, LABEL$2);
|
|
1553
|
-
const xai = parseXaiMediaOptions(options.providerOptions?.xai, xaiSpeechOptionsSchema,
|
|
1562
|
+
const xai = parseXaiMediaOptions(options.providerOptions?.xai, xaiSpeechOptionsSchema, XAI_SPEECH_HONORED, LABEL$2);
|
|
1554
1563
|
const warnings = [];
|
|
1555
1564
|
let format;
|
|
1556
1565
|
if (options.outputFormat !== void 0) {
|
|
@@ -1580,6 +1589,7 @@ function createQgridSpeech(modelId, config) {
|
|
|
1580
1589
|
if (options.voice !== void 0) requestParams.voice = options.voice;
|
|
1581
1590
|
if (options.language !== void 0) requestParams.language = options.language;
|
|
1582
1591
|
if (format !== void 0) requestParams.format = format;
|
|
1592
|
+
if (xai.options?.withTimestamps === true) requestParams.withTimestamps = true;
|
|
1583
1593
|
if (projectName) requestParams.projectName = projectName;
|
|
1584
1594
|
const params = {
|
|
1585
1595
|
...requestParams,
|
|
@@ -1601,6 +1611,8 @@ function createQgridSpeech(modelId, config) {
|
|
|
1601
1611
|
tokenName: data.tokenName
|
|
1602
1612
|
};
|
|
1603
1613
|
if (data.requestLogId !== void 0) qgridMetadata.requestLogId = data.requestLogId;
|
|
1614
|
+
if (data.audioTimestamps) qgridMetadata.audioTimestamps = data.audioTimestamps;
|
|
1615
|
+
if (data.durationSec !== void 0) qgridMetadata.duration = data.durationSec;
|
|
1604
1616
|
return {
|
|
1605
1617
|
audio: base64ToBytes(data.audio),
|
|
1606
1618
|
warnings,
|