@openclaw/liquid-ai-provider 0.0.0-stage → 2026.11.5-beta.1

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.md CHANGED
@@ -1,3 +1,7 @@
1
- # Temporary Holding Version
1
+ # OpenClaw Liquid AI Provider
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Native Liquid AI provider for the `d1` and `d1-ultra` Decision models.
4
+
5
+ This is an unpublished plugin candidate. Use a development checkout containing
6
+ the Decision V2 contract. See <https://docs.openclaw.ai/providers/liquid-ai> for
7
+ source-checkout setup, credentials, model selection, and supported inputs.
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
2
+ <path fill="currentColor" d="M50 7C38 24 22 40 22 59a28 28 0 0 0 56 0C78 40 62 24 50 7Zm0 72a20 20 0 0 1-20-20c0-12 10-24 20-37 10 13 20 25 20 37a20 20 0 0 1-20 20Z"/>
3
+ <path fill="currentColor" d="M46 38h8v13h11v8H54v13h-8V59H35v-8h11V38Z"/>
4
+ </svg>
Binary file
@@ -0,0 +1 @@
1
+ {"version":3,"entries":[{"output":"decision-provider.js","source":"extensions/liquid-ai/decision-provider.ts"},{"output":"index.js","source":"extensions/liquid-ai/index.ts"}],"chunks":[{"output":"decision-provider.js","sha256":"43b32dcadf7ee42ff05ff26bb6128f829a8e7b18eaecc881a2b7fc4b1d4eb0b0","facadeSource":"extensions/liquid-ai/decision-provider.ts","sources":["extensions/liquid-ai/decision-provider.ts"]},{"output":"index.js","sha256":"63d25af989cc6aa25214a1013f8023a909681d6d86f177803beecb4e7fb7f56d","facadeSource":"extensions/liquid-ai/index.ts","sources":["extensions/liquid-ai/index.ts"]}],"unrenderedSources":[]}
@@ -0,0 +1,161 @@
1
+ import { resolveAgentConfig } from "openclaw/plugin-sdk/agent-scope-runtime";
2
+ import { decodeSystemOneResponse, encodeSystemOneQuestions, validateDecisionBatchV2, validateDecisionResultV2 } from "openclaw/plugin-sdk/decisions";
3
+ import { fetchWithProviderHttpGuard, readProviderJsonResponse, resolveProviderHttpRequestConfig, resolveProviderTransportSsrFPolicy, sanitizeConfiguredModelProviderRequest } from "openclaw/plugin-sdk/provider-http";
4
+ // extensions/liquid-ai/decision-provider.ts
5
+ const BASE_URL = "https://api.liquid.ai/decisions/v1";
6
+ const MAX_REQUEST_BYTES = 45e5;
7
+ function hasUnsupportedParams(context) {
8
+ const { config, agentId, model } = context;
9
+ const agent = agentId ? resolveAgentConfig(config, agentId) : void 0;
10
+ const modelRef = `liquid-ai/${model.id}`;
11
+ return [
12
+ config.models?.providers?.["liquid-ai"]?.params,
13
+ model.params,
14
+ config.agents?.defaults?.params,
15
+ config.agents?.defaults?.models?.[modelRef]?.params,
16
+ agent?.models?.[modelRef]?.params,
17
+ agent?.params
18
+ ].some((params) => Object.values(params ?? {}).some((value) => value !== void 0));
19
+ }
20
+ function supportsBatch(batch) {
21
+ if (!validateDecisionBatchV2(batch) || batch.state.type === "list") return false;
22
+ if (batch.state.type === "json" && (batch.state.value === null || typeof batch.state.value !== "string" && typeof batch.state.value !== "object")) return false;
23
+ return Object.values(batch.questions).every((question) => {
24
+ if (question.type === "boolean") return question.instructions != null || Object.values(question.criteria ?? {}).some((value) => value != null);
25
+ return question.type === "choice" || question.type === "score";
26
+ });
27
+ }
28
+ function buildLiquidDecisionProvider(deps) {
29
+ return {
30
+ id: "liquid-ai",
31
+ contractVersion: 2,
32
+ async evaluate(batch, context) {
33
+ context.signal.throwIfAborted();
34
+ if (!["d1", "d1-ultra"].includes(context.model.id) || context.reasoning !== void 0 && context.reasoning !== "auto" || hasUnsupportedParams(context) || !supportsBatch(batch)) return {
35
+ status: "unavailable",
36
+ reason: "unsupported-input"
37
+ };
38
+ if (!context.auth.apiKey && !context.model.headers) return {
39
+ status: "unavailable",
40
+ reason: "credentials-unavailable"
41
+ };
42
+ const body = JSON.stringify({
43
+ model: context.model.id,
44
+ state: batch.state.type === "text" ? batch.state.text : batch.state.type === "json" ? batch.state.value : batch.state.type === "image" ? batch.state.text ?? "" : void 0,
45
+ ...batch.state.type === "image" ? { images: [batch.state.dataUri] } : {},
46
+ questions: encodeSystemOneQuestions(batch)
47
+ });
48
+ if (Buffer.byteLength(body) >= MAX_REQUEST_BYTES) return {
49
+ status: "unavailable",
50
+ reason: "unsupported-input"
51
+ };
52
+ let release;
53
+ let admissionError;
54
+ const assertActive = () => {
55
+ context.signal.throwIfAborted();
56
+ let admitted;
57
+ try {
58
+ admitted = context.isAdmissible();
59
+ } catch (error) {
60
+ admissionError = { error };
61
+ throw error;
62
+ }
63
+ if (!admitted || performance.now() >= context.deadlineMonotonicMs) throw new Error("Liquid Decision is no longer admitted.");
64
+ };
65
+ try {
66
+ assertActive();
67
+ if (batch.state.type === "image") {
68
+ const metadata = await deps.getImageMetadata(Buffer.from(batch.state.dataUri.split(",", 2)[1], "base64"));
69
+ assertActive();
70
+ if (!metadata || metadata.width <= 0 || metadata.height <= 0 || Math.max(metadata.width, metadata.height) > 100 * Math.min(metadata.width, metadata.height) || Math.ceil(metadata.width / 32) * Math.ceil(metadata.height / 32) > 1e4) return {
71
+ status: "unavailable",
72
+ reason: "unsupported-input"
73
+ };
74
+ }
75
+ const configured = context.config.models?.providers?.["liquid-ai"];
76
+ const configuredRequest = configured?.request;
77
+ const request = sanitizeConfiguredModelProviderRequest(configuredRequest ? {
78
+ proxy: configuredRequest.proxy,
79
+ tls: configuredRequest.tls,
80
+ allowPrivateNetwork: configuredRequest.allowPrivateNetwork
81
+ } : void 0);
82
+ const requestConfig = resolveProviderHttpRequestConfig({
83
+ provider: "liquid-ai",
84
+ capability: "llm",
85
+ transport: "http",
86
+ baseUrl: context.model.baseUrl ?? configured?.baseUrl ?? BASE_URL,
87
+ defaultBaseUrl: BASE_URL,
88
+ defaultHeaders: {
89
+ "Content-Type": "application/json",
90
+ ...context.auth.apiKey && (!configuredRequest?.auth || configuredRequest.auth.mode === "provider-default") ? { Authorization: `Bearer ${context.auth.apiKey}` } : {}
91
+ },
92
+ headers: context.model.headers,
93
+ request
94
+ });
95
+ const url = requestConfig.baseUrl.replace(/\/+$/u, "") + "/systemone";
96
+ assertActive();
97
+ const guarded = await fetchWithProviderHttpGuard({
98
+ url,
99
+ init: {
100
+ method: "POST",
101
+ headers: requestConfig.headers,
102
+ body
103
+ },
104
+ signal: context.signal,
105
+ timeoutMs: context.deadlineMonotonicMs - performance.now(),
106
+ beforeRequest: assertActive,
107
+ policy: resolveProviderTransportSsrFPolicy({
108
+ baseUrl: requestConfig.baseUrl,
109
+ url,
110
+ allowPrivateNetwork: requestConfig.allowPrivateNetwork,
111
+ trustConfiguredBaseUrlOrigin: configuredRequest?.allowPrivateNetwork !== false
112
+ }),
113
+ dispatcherPolicy: requestConfig.dispatcherPolicy,
114
+ auditContext: "liquid-ai-decisions"
115
+ });
116
+ release = guarded.release;
117
+ const response = guarded.response;
118
+ if (!response.ok) return {
119
+ status: "unavailable",
120
+ reason: response.status === 401 || response.status === 403 ? "authentication" : response.status === 429 ? "rate-limited" : [
121
+ 400,
122
+ 413,
123
+ 422
124
+ ].includes(response.status) ? "unsupported-input" : "transport"
125
+ };
126
+ let value;
127
+ try {
128
+ value = await readProviderJsonResponse(response, "Liquid decisions", {
129
+ maxBytes: 1048576,
130
+ timeoutMs: Math.max(1, context.deadlineMonotonicMs - performance.now())
131
+ });
132
+ } catch {
133
+ context.signal.throwIfAborted();
134
+ return {
135
+ status: "unavailable",
136
+ reason: "invalid-response"
137
+ };
138
+ }
139
+ assertActive();
140
+ const result = decodeSystemOneResponse(batch, value);
141
+ return validateDecisionResultV2(batch, result, context.model.inference?.decision) && result.usage?.outputTokens === 0 ? {
142
+ status: "ok",
143
+ result
144
+ } : {
145
+ status: "unavailable",
146
+ reason: "invalid-response"
147
+ };
148
+ } catch {
149
+ context.signal.throwIfAborted();
150
+ if (admissionError) throw admissionError.error;
151
+ return {
152
+ status: "unavailable",
153
+ reason: "transport"
154
+ };
155
+ } finally {
156
+ await release?.();
157
+ }
158
+ }
159
+ };
160
+ }
161
+ export { buildLiquidDecisionProvider };
package/dist/index.js ADDED
@@ -0,0 +1,28 @@
1
+ import { buildLiquidDecisionProvider } from "./decision-provider.js";
2
+ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
3
+ import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
4
+ // extensions/liquid-ai/index.ts
5
+ var liquid_ai_default = definePluginEntry({
6
+ id: "liquid-ai",
7
+ name: "Liquid AI Provider",
8
+ description: "Liquid AI native Decision provider",
9
+ register(api) {
10
+ api.registerProvider({
11
+ id: "liquid-ai",
12
+ label: "Liquid AI",
13
+ docsPath: "/providers/liquid-ai",
14
+ envVars: ["LIQUIDAI_API_KEY"],
15
+ auth: [createProviderApiKeyAuthMethod({
16
+ providerId: "liquid-ai",
17
+ methodId: "api-key",
18
+ label: "Liquid AI API key",
19
+ optionKey: "liquidAiApiKey",
20
+ flagName: "--liquid-ai-api-key",
21
+ envVar: "LIQUIDAI_API_KEY",
22
+ promptMessage: "Enter Liquid AI API key"
23
+ })]
24
+ });
25
+ api.registerDecisionProvider(buildLiquidDecisionProvider({ getImageMetadata: (buffer) => api.runtime.media.getImageMetadata(buffer) }));
26
+ }
27
+ });
28
+ export { liquid_ai_default as default };
@@ -0,0 +1,94 @@
1
+ {
2
+ "id": "liquid-ai",
3
+ "categories": ["models"],
4
+ "activation": { "onStartup": false },
5
+ "enabledByDefault": true,
6
+ "providers": ["liquid-ai"],
7
+ "providerEndpoints": [
8
+ { "endpointClass": "liquid-ai-native", "hosts": ["api.liquid.ai"] }
9
+ ],
10
+ "providerRequest": { "providers": { "liquid-ai": { "family": "liquid-ai" } } },
11
+ "modelCatalog": {
12
+ "providers": {
13
+ "liquid-ai": {
14
+ "baseUrl": "https://api.liquid.ai/decisions/v1",
15
+ "models": [
16
+ {
17
+ "id": "d1",
18
+ "name": "Liquid AI d1",
19
+ "input": ["text", "image"],
20
+ "inference": {
21
+ "chat": false,
22
+ "decision": {
23
+ "protocol": "liquid-ai-systemone",
24
+ "input": ["text", "image"],
25
+ "questions": {
26
+ "boolean": { "probabilities": "boolean", "abstention": false },
27
+ "choice": { "probabilities": "categorical", "abstention": false, "minOptions": 2 },
28
+ "score": { "probabilities": "categorical", "abstention": false, "minOptions": 2 }
29
+ },
30
+ "confidence": "provider-specific",
31
+ "reasoning": { "modes": ["auto"] },
32
+ "limits": {
33
+ "image": {
34
+ "maxBytes": 4194304,
35
+ "mimeTypes": ["image/png", "image/jpeg", "image/webp"],
36
+ "remoteUrls": false
37
+ }
38
+ },
39
+ "billing": { "unit": "tokens", "source": "provider-docs", "usdPerMillion": { "output": 0 } }
40
+ }
41
+ }
42
+ },
43
+ {
44
+ "id": "d1-ultra",
45
+ "name": "Liquid AI d1 Ultra",
46
+ "input": ["text", "image"],
47
+ "inference": {
48
+ "chat": false,
49
+ "decision": {
50
+ "protocol": "liquid-ai-systemone",
51
+ "input": ["text", "image"],
52
+ "questions": {
53
+ "boolean": { "probabilities": "boolean", "abstention": false },
54
+ "choice": { "probabilities": "categorical", "abstention": false, "minOptions": 2 },
55
+ "score": { "probabilities": "categorical", "abstention": false, "minOptions": 2 }
56
+ },
57
+ "confidence": "provider-specific",
58
+ "reasoning": { "modes": ["auto"] },
59
+ "limits": {
60
+ "image": {
61
+ "maxBytes": 4194304,
62
+ "mimeTypes": ["image/png", "image/jpeg", "image/webp"],
63
+ "remoteUrls": false
64
+ }
65
+ },
66
+ "billing": { "unit": "tokens", "source": "provider-docs", "usdPerMillion": { "input": 0.042, "output": 0 } }
67
+ }
68
+ }
69
+ }
70
+ ]
71
+ }
72
+ }
73
+ },
74
+ "setup": { "providers": [{ "id": "liquid-ai", "envVars": ["LIQUIDAI_API_KEY"] }] },
75
+ "providerAuthChoices": [
76
+ {
77
+ "provider": "liquid-ai",
78
+ "method": "api-key",
79
+ "choiceId": "liquid-ai-api-key",
80
+ "appGuidedSecret": true,
81
+ "credentialOnly": true,
82
+ "choiceLabel": "Liquid AI API key",
83
+ "groupId": "liquid-ai",
84
+ "groupLabel": "Liquid AI",
85
+ "groupHint": "Native Decision models",
86
+ "optionKey": "liquidAiApiKey",
87
+ "cliFlag": "--liquid-ai-api-key",
88
+ "cliOption": "--liquid-ai-api-key <key>",
89
+ "cliDescription": "Liquid AI API key"
90
+ }
91
+ ],
92
+ "contracts": { "decisionProviders": ["liquid-ai"] },
93
+ "configSchema": { "type": "object", "additionalProperties": false, "properties": {} }
94
+ }
package/package.json CHANGED
@@ -1,6 +1,51 @@
1
1
  {
2
2
  "name": "@openclaw/liquid-ai-provider",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "2026.11.5-beta.1",
4
+ "description": "OpenClaw Liquid AI provider plugin.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/openclaw/openclaw"
8
+ },
9
+ "type": "module",
10
+ "openclaw": {
11
+ "extensions": [
12
+ "./index.ts"
13
+ ],
14
+ "install": {
15
+ "clawhubSpec": "clawhub:@openclaw/liquid-ai-provider",
16
+ "npmSpec": "@openclaw/liquid-ai-provider",
17
+ "defaultChoice": "npm",
18
+ "minHostVersion": ">=2026.10.6"
19
+ },
20
+ "compat": {
21
+ "pluginApi": ">=2026.11.5-beta.1"
22
+ },
23
+ "build": {
24
+ "openclawVersion": "2026.11.5-beta.1",
25
+ "bundledDist": false
26
+ },
27
+ "release": {
28
+ "publishToClawHub": true,
29
+ "publishToNpm": true
30
+ },
31
+ "runtimeExtensions": [
32
+ "./dist/index.js"
33
+ ]
34
+ },
35
+ "files": [
36
+ "dist/**",
37
+ "openclaw.plugin.json",
38
+ "README.md",
39
+ "assets/icon.png",
40
+ "assets/activity.svg"
41
+ ],
42
+ "peerDependencies": {
43
+ "openclaw": ">=2026.11.5-beta.1"
44
+ },
45
+ "peerDependenciesMeta": {
46
+ "openclaw": {
47
+ "optional": true
48
+ }
49
+ },
50
+ "bundledDependencies": []
51
+ }