@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 +6 -2
- package/assets/activity.svg +4 -0
- package/assets/icon.png +0 -0
- package/dist/.source-module-urls/605ee6ba9f7f64cc9a2cb1f0022e16ab91236fa3a81513ac73855349ee5bc039.json +1 -0
- package/dist/decision-provider.js +161 -0
- package/dist/index.js +28 -0
- package/openclaw.plugin.json +94 -0
- package/package.json +49 -4
package/README.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# OpenClaw Liquid AI Provider
|
|
2
2
|
|
|
3
|
-
|
|
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>
|
package/assets/icon.png
ADDED
|
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": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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
|
+
}
|