@hypit/hypit 0.2.4 → 0.2.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypit/hypit",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "homepage": "https://hypit.ai",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,8 +1,8 @@
1
1
  # `@hypit/provider-monid`
2
2
 
3
- Hypit Runtime Provider for a [Monid](https://monid.ai) workspace. It runs Monid's `bytedance`
4
- Seedance endpoints through `POST /v1/run`, polls `GET /v1/runs/{runId}` until the run is terminal,
5
- downloads the returned `video_url` and stores the video in the current Build.
3
+ Hypit Runtime Provider for a [Monid](https://monid.ai) workspace. It runs Monid's generation
4
+ endpoints through `POST /v1/run`, polls `GET /v1/runs/{runId}` until the run is terminal, downloads
5
+ the returned video and stores it in the current Build.
6
6
 
7
7
  | Capability | Monid endpoint |
8
8
  | --- | --- |
@@ -10,20 +10,32 @@ downloads the returned `video_url` and stores the video in the current Build.
10
10
  | `@hypit/seedance@1#seedance-2-fast` | `bytedance` `/v1/video/seedance-2.0-fast` |
11
11
  | `@hypit/seedance@1#seedance-2-mini` | `bytedance` `/v1/video/seedance-2.0-mini` |
12
12
  | `@hypit/seedance@1#seedance-2.5` | `bytedance` `/v1/video/seedance-2.5` |
13
+ | `@hypit/minimax-h3@1#minimax-h3` | `minimax` `/v1/video/minimax-h3` |
13
14
 
14
- Monid's catalogue also lists MiniMax H3 and other generation endpoints; their input schemas are
15
- published only through the authenticated `inspect` operation, so this Provider maps the Seedance
16
- endpoints whose request body Monid documents publicly.
17
-
18
- The run `input` is the BytePlus ModelArk request the endpoint relays: one `content` array holding
19
- the prompt and each media input as a typed item with its `role` (`first_frame`, `last_frame`,
20
- `reference_image`, `reference_video`, `reference_audio`), then `resolution`, `ratio`, `duration` and
21
- `generate_audio`. Monid documents no web search field for these endpoints, so `web-search="true"`
22
- is unsupported. Seedance 2.5 frame mode (`first-frame` present) requires `aspect-ratio="adaptive"`.
23
- Seedance visual references may carry `person-reference`; the Provider accepts the declaration and
24
- transmits nothing for it, since the endpoint has no field for it. Seedance rejects reference images
25
- and videos that contain a real human face; Monid offers no way to register authorized portrait
26
- material, so such a request fails with the upstream moderation error.
15
+ Monid's catalogue lists further generation endpoints, including the H3 Fast, Max and Max Turbo
16
+ variants and Hailuo 2.3; their models are not the ones the Distribution describes. Input schemas
17
+ are published through the authenticated `inspect` operation, which is where the request bodies
18
+ above come from.
19
+
20
+ Every mapped endpoint relays a BytePlus ModelArk request: one `content` array holding the prompt
21
+ and each media input as a typed item with its `role` (`first_frame`, `last_frame`,
22
+ `reference_image`, `reference_video`, `reference_audio`), then `resolution`, `ratio` and
23
+ `duration`.
24
+
25
+ The Seedance endpoints add `generate_audio`. Monid documents no web search field for them, so
26
+ `web-search="true"` is unsupported, and Seedance 2.5 frame mode (`first-frame` present) requires
27
+ `aspect-ratio="adaptive"`. Seedance visual references may carry `person-reference`; the Provider
28
+ accepts the declaration and transmits nothing for it, since the endpoint has no field for it.
29
+ Seedance rejects reference images and videos that contain a real human face; Monid offers no way to
30
+ register authorized portrait material, so such a request fails with the upstream moderation error.
31
+
32
+ MiniMax H3 names its model in the body and takes neither of those two fields. The endpoint requires
33
+ a resolution, so a request that states none is sent at `2K`, the resolution the HypiHub Provider
34
+ also selects. It also requires a ratio that is not `adaptive` for text-to-video, while frame mode
35
+ resolves the framing from the uploaded image and reference-to-video defaults to adaptive: a
36
+ text-to-video request carrying no `aspect-ratio` is reported unsupported before any reference is
37
+ resolved, and the other two modes are sent as `adaptive`. H3 returns its video at `content.url`
38
+ rather than the ModelArk `video_url`.
27
39
 
28
40
  Reference media are uploaded through the workspace file system Monid provides for this purpose
29
41
  (`sfs`): `/put` signs an upload for `hypit/<resource>.<ext>`, the bytes are `PUT` to that URL, and
@@ -9,6 +9,7 @@
9
9
  "dependencies": {
10
10
  "@hypit/endpoint-kit": "workspace:*",
11
11
  "@hypit/generation": "workspace:*",
12
+ "@hypit/minimax-h3": "workspace:*",
12
13
  "@hypit/protocol": "workspace:*",
13
14
  "@hypit/runtime": "workspace:*",
14
15
  "@hypit/runtime-kit": "workspace:*"
@@ -2,13 +2,21 @@ import type { ModuleRef } from "@hypit/protocol";
2
2
  import type { GenerationWireMapping } from "@hypit/generation";
3
3
 
4
4
  const SEEDANCE: ModuleRef = { name: "@hypit/seedance", version: "1" };
5
+ const MINIMAX_H3: ModuleRef = { name: "@hypit/minimax-h3", version: "1" };
6
+
7
+ /**
8
+ * One mapping plus the Monid provider that relays the endpoint. Monid addresses an endpoint by
9
+ * provider and path, so the provider travels with the mapping rather than being assumed.
10
+ */
11
+ export type MonidMapping = GenerationWireMapping & { readonly service: string };
5
12
 
6
13
  /**
7
14
  * Monid `bytedance` endpoints and the fields their ModelArk request body takes. Media fields name
8
15
  * the `role` of a `content` item; routes.ts folds them into that array. `personReference` is
9
16
  * accepted on visual references and not transmitted: the endpoint has no field for it.
10
17
  */
11
- const seedance = (name: string, endpoint: string): GenerationWireMapping => ({
18
+ const seedance = (name: string, endpoint: string): MonidMapping => ({
19
+ service: "bytedance",
12
20
  capability: { module: SEEDANCE, name }, result: "video", routes: [{ model: endpoint }],
13
21
  fields: {
14
22
  prompt: { as: "value", field: "text" },
@@ -25,9 +33,34 @@ const seedance = (name: string, endpoint: string): GenerationWireMapping => ({
25
33
  },
26
34
  });
27
35
 
28
- export const monidMappings: readonly GenerationWireMapping[] = [
36
+ /**
37
+ * Monid's `minimax` MiniMax-H3 endpoint. It takes the same role-tagged `content` array as the
38
+ * ModelArk endpoints above, names the model in the body, and carries neither a generated-audio nor
39
+ * a web-search field. `resolution` is required by the endpoint while the model's port is optional,
40
+ * so an unstated resolution is sent as the 2K the HypiHub Provider also selects.
41
+ */
42
+ const minimaxH3: MonidMapping = {
43
+ service: "minimax",
44
+ capability: { module: MINIMAX_H3, name: "minimax-h3" }, result: "video",
45
+ routes: [{ model: "/v1/video/minimax-h3" }],
46
+ constants: { model: "MiniMax-H3" },
47
+ fields: {
48
+ prompt: { as: "value", field: "text" },
49
+ referenceImage: { as: "urlArray", field: "reference_image" },
50
+ referenceVideo: { as: "urlArray", field: "reference_video" },
51
+ referenceAudio: { as: "urlArray", field: "reference_audio" },
52
+ firstFrame: { as: "url", field: "first_frame" },
53
+ lastFrame: { as: "url", field: "last_frame" },
54
+ resolution: { as: "value", field: "resolution", whenAbsent: "2K" },
55
+ aspectRatio: { as: "value", field: "ratio" },
56
+ duration: { as: "value", field: "duration" },
57
+ },
58
+ };
59
+
60
+ export const monidMappings: readonly MonidMapping[] = [
29
61
  seedance("seedance-2", "/v1/video/seedance-2.0"),
30
62
  seedance("seedance-2-fast", "/v1/video/seedance-2.0-fast"),
31
63
  seedance("seedance-2-mini", "/v1/video/seedance-2.0-mini"),
32
64
  seedance("seedance-2.5", "/v1/video/seedance-2.5"),
65
+ minimaxH3,
33
66
  ];
@@ -65,11 +65,14 @@ function httpsUrl(value: unknown, subject: string): string {
65
65
  assert(typeof value === "string" && /^https?:\/\//u.test(value), `${subject} has no URL`);
66
66
  return value;
67
67
  }
68
- /** The relayed ModelArk result; Monid returns the task's `video_url` in the run output. */
68
+ /**
69
+ * The relayed result. The ModelArk endpoints return the task's `video_url`; MiniMax-H3 returns the
70
+ * video at `content.url`. The link expires, so collection downloads it rather than storing it.
71
+ */
69
72
  function outputVideoUrl(run: Record<string, unknown>): string {
70
73
  const output = object(run.output, "Monid run output");
71
- const nested = output.content !== undefined ? object(output.content, "Monid run output content").video_url : undefined;
72
- return httpsUrl(output.video_url ?? nested, "Monid run output");
74
+ const content = output.content === undefined ? undefined : object(output.content, "Monid run output content");
75
+ return httpsUrl(output.video_url ?? content?.video_url ?? content?.url, "Monid run output");
73
76
  }
74
77
  const extensions: Readonly<Record<string, string>> = {
75
78
  "image/jpeg": "jpg", "image/png": "png", "image/webp": "webp", "video/mp4": "mp4", "video/quicktime": "mov",
@@ -160,7 +163,7 @@ function endpoint(client: MonidClient, pollIntervalMs: number, maxOperationMs: n
160
163
  const route = monidRouteForCapability(context.need.capability);
161
164
  assert(route !== undefined, "Monid does not implement this exact capability");
162
165
  const request = route.prepare(context.need.constraints);
163
- await context.reportProgress?.({ phase: `Preparing Monid request: bytedance ${request.endpoint}` });
166
+ await context.reportProgress?.({ phase: `Preparing Monid request: ${request.service} ${request.endpoint}` });
164
167
  let input: Record<string, unknown>;
165
168
  try {
166
169
  input = await request.compile(resolverFor(client, context, publicAssetUrl));
@@ -168,8 +171,8 @@ function endpoint(client: MonidClient, pollIntervalMs: number, maxOperationMs: n
168
171
  throw new MonidServiceError(error instanceof MonidServiceError ? error.code : "MONID_ERROR",
169
172
  `Monid request preparation failed; endpoint=${request.endpoint}; generation not submitted: ${failureMessage(error)}`);
170
173
  }
171
- await context.reportProgress?.({ phase: `Submitting Monid request: bytedance ${request.endpoint}` });
172
- const { body: run } = await client.run({ provider: "bytedance", endpoint: request.endpoint, input }, apiKey(context.credentials));
174
+ await context.reportProgress?.({ phase: `Submitting Monid request: ${request.service} ${request.endpoint}` });
175
+ const { body: run } = await client.run({ provider: request.service, endpoint: request.endpoint, input }, apiKey(context.credentials));
173
176
  const handle: Handle = { contract: "hypit.monid-operation@1", runId: runId(run), route: route.key, startedAt: Date.now() };
174
177
  const receipt = { id: handle.runId };
175
178
  const ended = monidTerminalStatuses.includes(String(run.status) as typeof monidTerminalStatuses[number]);
@@ -230,7 +233,7 @@ export function createMonidProvider(options: CreateMonidProviderOptions = {}) {
230
233
  const asyncEndpoint = endpoint(client, pollIntervalMs, operationTimeoutMs, options.publicAssetUrl);
231
234
  return defineEndpointPackage({
232
235
  module: monidProviderModuleRef, facet: "gateway", instance: options.instance ?? "monid.default", pool: options.pool ?? options.instance ?? "monid.default",
233
- pricing: { kind: "page", url: "https://monid.ai/tools/bytedance" },
236
+ pricing: { kind: "page", url: "https://monid.ai/tools" },
234
237
  credentials: { apiKey: options.apiKey ?? credentialRef("os", "monid.api-key") },
235
238
  credentialInputs: { apiKey: { label: "Monid API key" } },
236
239
  defaultConcurrency: options.defaultConcurrency ?? 4,
@@ -4,19 +4,22 @@ import {
4
4
  generationTypes,
5
5
  sealGeneratedVideoSet,
6
6
  } from "@hypit/generation";
7
- import type { GenerationArtifactUrlResolver, GenerationRequest, GenerationWireMapping } from "@hypit/generation";
7
+ import type { GenerationArtifactUrlResolver, GenerationRequest } from "@hypit/generation";
8
8
  import { canonicalize } from "@hypit/protocol";
9
9
  import type { BlobRef, CapabilityRef, CanonicalValue, StoredValue, TypeRef } from "@hypit/protocol";
10
10
  import type { EndpointRequest, EndpointSupport } from "@hypit/endpoint-kit";
11
11
  import { monidMappings } from "./mapping.js";
12
+ import type { MonidMapping } from "./mapping.js";
12
13
 
13
14
  export type MonidPreparedRequest = {
14
- /** Monid endpoint path under the `bytedance` provider. */
15
+ /** Monid provider relaying the endpoint. */
16
+ readonly service: string;
17
+ /** Monid endpoint path under that provider. */
15
18
  readonly endpoint: string;
16
19
  readonly compile: (resolve: GenerationArtifactUrlResolver) => Promise<Record<string, unknown>>;
17
20
  };
18
21
 
19
- export type MonidRoute = GenerationWireMapping & {
22
+ export type MonidRoute = MonidMapping & {
20
23
  readonly key: string;
21
24
  readonly returns: TypeRef;
22
25
  readonly supports: (request: EndpointRequest) => EndpointSupport;
@@ -35,7 +38,21 @@ function strings(value: unknown): readonly string[] {
35
38
  return Array.isArray(value) ? value.filter((item): item is string => typeof item === "string" && item.length > 0) : [];
36
39
  }
37
40
 
38
- function rejection(mapping: GenerationWireMapping, request: GenerationRequest): string | undefined {
41
+ function referenceToVideo(request: GenerationRequest): boolean {
42
+ return present(request, "referenceImage") || present(request, "referenceVideo") || present(request, "referenceAudio");
43
+ }
44
+
45
+ function rejection(mapping: MonidMapping, request: GenerationRequest): string | undefined {
46
+ if (mapping.capability.name === "minimax-h3") {
47
+ // MiniMax-H3 resolves the framing from the uploaded image in frame mode and defaults
48
+ // reference-to-video to adaptive. Text-to-video carries no such source, and the endpoint
49
+ // takes no adaptive ratio there, so the aspect ratio has to be stated.
50
+ if (!present(request, "firstFrame") && !present(request, "lastFrame") && !referenceToVideo(request)
51
+ && scalar(request, "aspectRatio") === undefined) {
52
+ return "MiniMax H3 text-to-video on Monid requires an aspect ratio";
53
+ }
54
+ return undefined;
55
+ }
39
56
  if (scalar(request, "webSearch") === true) {
40
57
  return "Monid's Seedance endpoints document no web search field";
41
58
  }
@@ -54,6 +71,7 @@ function arkInput(input: Record<string, unknown>): Record<string, unknown> {
54
71
  const first = input.first_frame;
55
72
  const last = input.last_frame;
56
73
  return {
74
+ ...(typeof input.model === "string" ? { model: input.model } : {}),
57
75
  content: [
58
76
  { type: "text", text: input.text },
59
77
  ...(typeof first === "string" ? [contentItem("image_url", first, "first_frame")] : []),
@@ -69,6 +87,15 @@ function arkInput(input: Record<string, unknown>): Record<string, unknown> {
69
87
  };
70
88
  }
71
89
 
90
+ /**
91
+ * MiniMax-H3 takes a required ratio: frame mode and reference-to-video both resolve to adaptive,
92
+ * and rejection() has already required a stated ratio for text-to-video.
93
+ */
94
+ function minimaxH3Input(input: Record<string, unknown>): Record<string, unknown> {
95
+ const body = arkInput(input);
96
+ return { ...body, ratio: body.ratio ?? "adaptive" };
97
+ }
98
+
72
99
  function capabilityKey(capability: CapabilityRef): string {
73
100
  return `${capability.module.name}@${capability.module.version}#${capability.name}`;
74
101
  }
@@ -85,9 +112,11 @@ export const monidRoutes: readonly MonidRoute[] = monidMappings.map((mapping) =>
85
112
  const request = constraints as unknown as GenerationRequest;
86
113
  const reason = rejection(mapping, request);
87
114
  if (reason !== undefined) throw new Error(reason);
115
+ const body = mapping.capability.name === "minimax-h3" ? minimaxH3Input : arkInput;
88
116
  return {
117
+ service: mapping.service,
89
118
  endpoint: selectWireModelForRequest(mapping, request),
90
- compile: async (resolve) => arkInput((await compileWireRequest(mapping, request, resolve)).input as Record<string, unknown>),
119
+ compile: async (resolve) => body((await compileWireRequest(mapping, request, resolve)).input as Record<string, unknown>),
91
120
  };
92
121
  },
93
122
  packageResult: (artifacts) => ({