@flovialabs/sdk 0.1.0 → 0.1.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/CHANGELOG.md +6 -0
- package/README.md +41 -15
- package/dist/cli.js +6 -3
- package/dist/client.d.ts +2 -1
- package/dist/client.js +2 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
- CLI generation output shows the response status and incomplete reason. A delivered partial answer still exits 0; no continuation is generated automatically.
|
|
6
|
+
- CLI status shows bought balance; usage shows server-provided bought totals and the generation subtotal without adding them twice. Free usage stays in percentages.
|
|
7
|
+
- Corrected consent, replay lifetime, onboarding and copyable usage examples in the README.
|
|
8
|
+
|
|
3
9
|
## 0.1.0
|
|
4
10
|
|
|
5
11
|
First release. Flovia is in alpha; APIs may change.
|
package/README.md
CHANGED
|
@@ -10,11 +10,13 @@ TypeScript client and command-line tool for [Flovia](https://api.flovia.dev). It
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
12
12
|
```sh
|
|
13
|
-
npm install @flovialabs/sdk
|
|
13
|
+
npm install --save-exact @flovialabs/sdk@0.1.1
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
## Your key
|
|
17
17
|
|
|
18
|
+
You need an invitation to [register and receive a Flovia key](https://api.flovia.dev/join). Keep the key private; it is shown once. Follow the [connection guide](https://api.flovia.dev/) to confirm your email and start the free allowance. Contact [Flovia support](https://flovia.dev/contact) if you need access or lose your key. Installing this package does not create an account or start an allowance.
|
|
19
|
+
|
|
18
20
|
Set your Flovia key (`flv_...`) in the environment:
|
|
19
21
|
|
|
20
22
|
```sh
|
|
@@ -28,7 +30,7 @@ Or pass it as `new FloviaClient({ apiKey })`. The client keeps the key private:
|
|
|
28
30
|
## Quick start
|
|
29
31
|
|
|
30
32
|
```ts
|
|
31
|
-
import { FloviaClient,
|
|
33
|
+
import { FloviaClient, newOperationId } from "@flovialabs/sdk";
|
|
32
34
|
|
|
33
35
|
const flovia = new FloviaClient(); // reads FLOVIA_KEY; base URL https://api.flovia.dev
|
|
34
36
|
|
|
@@ -37,7 +39,7 @@ const status = await flovia.status();
|
|
|
37
39
|
console.log(status.five_hour.used_pct, status.week.used_pct);
|
|
38
40
|
|
|
39
41
|
// Your lookups today (or since a time within the last 7 days)
|
|
40
|
-
const usage = await flovia.usageToday(
|
|
42
|
+
const usage = await flovia.usageToday(); // today, UTC
|
|
41
43
|
|
|
42
44
|
// Any tool: list them, then call one
|
|
43
45
|
const tools = await flovia.listTools();
|
|
@@ -45,15 +47,36 @@ const reply = await flovia.call("flovia_status");
|
|
|
45
47
|
|
|
46
48
|
// A text generation (uses credits). Save the operation id before sending.
|
|
47
49
|
const op = newOperationId();
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
+
const request = { model: "gpt-6-luna" as const, input: "Explain a DEX aggregator in one sentence." };
|
|
51
|
+
// Persist { op, request } in your application's private storage before the next line.
|
|
52
|
+
const g = await flovia.generate(request, { operationId: op });
|
|
53
|
+
console.log(g.response.status, g.response.incomplete_details, g.outputText);
|
|
50
54
|
```
|
|
51
55
|
|
|
56
|
+
### First data lookup
|
|
57
|
+
|
|
58
|
+
`listTools()` returns the tools offered to your account and their `inputSchema`. A catalog call sends nothing to the data provider. For example, list CoinGecko paths, then make one price lookup:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
await flovia.call("flovia_coingecko", {
|
|
62
|
+
path: "list", request_text: "Which prices can I read?", purpose: "choose a supported path",
|
|
63
|
+
});
|
|
64
|
+
const priceRequest = {
|
|
65
|
+
path: "/simple/price", query: { ids: "bitcoin", vs_currencies: "usd", include_last_updated_at: true },
|
|
66
|
+
request_text: "What is Bitcoin's USD price?", purpose: "check a price with its update time",
|
|
67
|
+
conversation_id: "sdk-price-example", operation_id: newOperationId(),
|
|
68
|
+
};
|
|
69
|
+
// Save priceRequest before sending; this lookup uses the shared allowance.
|
|
70
|
+
const price = await flovia.call("flovia_coingecko", priceRequest);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
If either call asks for a notice or consent, stop and follow the human-answer flow below. Read the returned source time, coverage and missing-value notes; a successful lookup is not a guarantee that it answers your question. Flovia exposes selected read-only paths, methods and public HTML hosts, not every feature of the upstream plans. Use the live catalog for the supported subset. OpenAI supports text generation with `gpt-6-luna`; it does not fetch other providers or offer image/audio generation.
|
|
74
|
+
|
|
52
75
|
### Operation ids and replay
|
|
53
76
|
|
|
54
77
|
`generate()` requires an operation id (8-128 characters of `A-Z a-z 0-9 _ . : -`). Create one with `newOperationId()` and store it with the request **before** sending.
|
|
55
78
|
|
|
56
|
-
If the reply is lost (timeout, network error, `executed: "unknown"`),
|
|
79
|
+
If the reply is lost (timeout, network error, `executed: "unknown"`), keep the request and **same** id. A completed or incomplete answer can normally be reopened for about **15 minutes** with the unchanged request, `replayed: true` and no additional charge. Follow `nextStep` for `operation_pending` or `upstream_unknown`; the answer may not yet be available. After `answer_expired` or `operation_retired`, Flovia cannot return the saved text. Never start a new id to recover a lost answer: that may run and charge twice. A genuinely new generation requires the person's decision to spend again.
|
|
57
80
|
|
|
58
81
|
## Errors
|
|
59
82
|
|
|
@@ -74,9 +97,9 @@ A refusal (`ok: false`) or a failed request throws `FloviaError`:
|
|
|
74
97
|
|
|
75
98
|
These two codes are thrown on purpose. They need the person's own answer, and the SDK never answers for them.
|
|
76
99
|
|
|
77
|
-
1. Show `error.reply.user_notice`
|
|
78
|
-
2.
|
|
79
|
-
3.
|
|
100
|
+
1. Show the full `error.reply.user_notice` unchanged. For `data_consent_needed`, also show `error.reply.consent.question`; the short question does not replace the notice.
|
|
101
|
+
2. For `data_consent_needed`, get the person's own answer through your normal flow. Call `flovia_data_consent` with `answer` (`"y"` or `"n"` from that answer), `consent_version` from `error.reply.consent.version`, and `user_words` containing their actual words. Do not substitute the current SDK version or a generated answer. `notice_first` alone does not ask for a consent answer or supply a consent version.
|
|
102
|
+
3. For `data_consent_needed`, repeat the unchanged call only after their `y` is accepted; with `n`, stop. For `notice_first`, repeat the same call after showing the notice, adding `notice_shown: true`.
|
|
80
103
|
|
|
81
104
|
Do not answer these automatically from code or from an agent.
|
|
82
105
|
|
|
@@ -105,17 +128,20 @@ export FLOVIA_KEY=flv_... # or: --key-file ~/.config/flovia/key
|
|
|
105
128
|
npx flovia status
|
|
106
129
|
npx flovia usage --days 3
|
|
107
130
|
npx flovia tools
|
|
108
|
-
npx flovia
|
|
109
|
-
npx flovia
|
|
110
|
-
npx flovia generate --op
|
|
131
|
+
npx flovia tools --json # includes each tool's inputSchema
|
|
132
|
+
npx flovia call flovia_usage_today '{}'
|
|
133
|
+
npx flovia generate --op sdk-example-0001 --max-output 300 "Explain a DEX aggregator."
|
|
134
|
+
# Only to reopen that exact request within about 15 minutes (not a new generation):
|
|
135
|
+
npx flovia generate --op sdk-example-0001 --max-output 300 --yes "Explain a DEX aggregator."
|
|
111
136
|
npx flovia help
|
|
112
137
|
```
|
|
113
|
-
These commands run after installing `@flovialabs/sdk` in the current project. Without a local installation, specify the package: `npx --package=@flovialabs/sdk@0.1.
|
|
138
|
+
These commands run after installing `@flovialabs/sdk` in the current project. Without a local installation, specify the package: `npx --package=@flovialabs/sdk@0.1.1 flovia --help`. Use a unique operation id for each new request; `sdk-example-0001` is only an example. Save the id, prompt and options before sending, and keep them unchanged for replay.
|
|
114
139
|
|
|
115
|
-
`usage` shows lookup counts
|
|
140
|
+
`status` shows bought balance. `usage` shows lookup counts, free percentages and server-provided bought totals; its generation subtotal is already included in the period total. Missing amounts show `-`, not zero. It does not add rounded endpoint percentages. To continue a listing, copy the printed command with its exact `--since` and `--cursor`; do not combine `--days` with `--cursor`.
|
|
116
141
|
|
|
117
142
|
- Every command takes `--json` (the raw reply), `--base-url URL`, `--timeout SECONDS` and `--key-file PATH`.
|
|
118
143
|
- `generate` uses credits. It needs `--op ID` or `--new-op`, and asks for confirmation on a terminal; add `--yes` in scripts.
|
|
144
|
+
- `generate` prints the response status and incomplete reason. `incomplete` / `max_output_tokens` means the delivered text stopped at the output limit, not that a continuation ran. Exit 0 means the response was delivered, not that the text is complete or correct. `--json` retains the full public response.
|
|
119
145
|
- Exit codes: 0 ok, 1 an error from Flovia, 2 a usage error.
|
|
120
146
|
|
|
121
147
|
### Safety
|
|
@@ -123,7 +149,7 @@ These commands run after installing `@flovialabs/sdk` in the current project. Wi
|
|
|
123
149
|
- The key is read only from `FLOVIA_KEY` or a key file, never from a command-line argument (other users can see arguments in `ps`). It is never printed.
|
|
124
150
|
- A key file readable by other users gets a warning; use `chmod 600`.
|
|
125
151
|
- `--base-url` must be `https`, so the key is never sent unencrypted. Plain `http` is allowed only for `localhost`, `127.0.0.1` and `[::1]`.
|
|
126
|
-
- On `notice_first` or `data_consent_needed` the CLI shows the notice
|
|
152
|
+
- On `notice_first` or `data_consent_needed` the CLI shows the full notice and, when present, the question, then stops. The person answers it themselves; `--yes` confirms spending, not data consent.
|
|
127
153
|
|
|
128
154
|
## License
|
|
129
155
|
|
package/dist/cli.js
CHANGED
|
@@ -124,6 +124,7 @@ export function renderStatus(s, expected, now = Date.now()) {
|
|
|
124
124
|
const lines = [`Flovia ${s.stage}, contract ${s.contract_version}${expected === null ? "" : ` (warning: this CLI expects ${expected})`}`];
|
|
125
125
|
for (const [label, w] of [["5-hour window", s.five_hour], ["week", s.week]])
|
|
126
126
|
lines.push(`${label.padEnd(14)} ${pct(w?.used_pct)} used${w?.resets_at ? `, resets ${when(w.resets_at, now)}` : label === "5-hour window" ? ", not started" : ""}`);
|
|
127
|
+
lines.push(`bought balance ${String(s["purchased_balance_display"] ?? "-")}`);
|
|
127
128
|
const pending = obj(s["free_pending"]);
|
|
128
129
|
if (pending["next_step"])
|
|
129
130
|
lines.push(`free use not started: ${String(pending["next_step"])}`);
|
|
@@ -150,6 +151,7 @@ export function renderUsage(u, today = false, now = Date.now()) {
|
|
|
150
151
|
const week = obj(u["free_week"]);
|
|
151
152
|
lines.push(`free week: ${pct(week["used_pct"])} used${week["resets_at"] ? `, resets ${when(week["resets_at"], now)}` : ""}`);
|
|
152
153
|
lines.push(`total in this period: ${String(obj(u["total"])["free_display"] ?? "-")} of the free week, ${String(u["count"] ?? 0)} lookups`);
|
|
154
|
+
lines.push(`bought credits used: ${String(obj(u["total"])["bought_display"] ?? "-")} total (includes generations)`);
|
|
153
155
|
// Endpoint percentages are rounded (0 can mean <1%); only counts can be summed by source.
|
|
154
156
|
const bySource = new Map();
|
|
155
157
|
for (const e of Array.isArray(u["by_endpoint"]) ? u["by_endpoint"] : []) {
|
|
@@ -159,7 +161,7 @@ export function renderUsage(u, today = false, now = Date.now()) {
|
|
|
159
161
|
if (bySource.size)
|
|
160
162
|
lines.push("", table([["source", "lookups"], ...[...bySource].map(([n, count]) => [n, String(count)])]).trimEnd());
|
|
161
163
|
const g = obj(u["generations"]);
|
|
162
|
-
lines.push("", `generations: ${String(g["operations"] ?? 0)} (completed ${String(g["completed"] ?? 0)}, incomplete ${String(g["incomplete"] ?? 0)}, unknown ${String(g["unknown"] ?? 0)}), ${String(g["free_display"] ?? "-")} of the free week`);
|
|
164
|
+
lines.push("", `generations: ${String(g["operations"] ?? 0)} (completed ${String(g["completed"] ?? 0)}, incomplete ${String(g["incomplete"] ?? 0)}, unknown ${String(g["unknown"] ?? 0)}), ${String(g["free_display"] ?? "-")} of the free week, bought ${String(g["bought_display"] ?? "-")} (included in total)`);
|
|
163
165
|
if (u.has_more)
|
|
164
166
|
lines.push("", `more lookups: rerun flovia usage with the same --key-file and --base-url options, adding --since ${String(u["since"])} --cursor ${String(u.next_cursor)}`);
|
|
165
167
|
return lines.join("\n") + "\n";
|
|
@@ -281,7 +283,7 @@ export async function run(argv, io) {
|
|
|
281
283
|
// The id is shown before anything is sent, so a lost reply can be reopened with it (never resent under a new one).
|
|
282
284
|
if (fresh)
|
|
283
285
|
err(`operation_id: ${id}\nSave it. To resend or reopen this request, use: --op ${id}\n`);
|
|
284
|
-
err("This generation uses Flovia credits (free windows first, then bought credits).
|
|
286
|
+
err("This generation uses Flovia credits (free windows first, then bought credits). The same --op and unchanged request can reopen a saved answer for about 15 minutes at no charge. Pending, unknown or expired answers must not be replaced with a new id.\n");
|
|
285
287
|
if (!p.flags.has("--yes")) {
|
|
286
288
|
if (io.confirm === null || text === "-")
|
|
287
289
|
throw new Usage("generate: add --yes to send (no terminal to confirm on)");
|
|
@@ -291,10 +293,11 @@ export async function run(argv, io) {
|
|
|
291
293
|
}
|
|
292
294
|
}
|
|
293
295
|
const g = await client.generate({ model: "gpt-6-luna", input, ...(max ? { max_output_tokens: max } : {}) }, { operationId: id });
|
|
296
|
+
const reason = obj(g.response["incomplete_details"])["reason"];
|
|
294
297
|
if (json)
|
|
295
298
|
print(g);
|
|
296
299
|
else
|
|
297
|
-
out(`${g.outputText}\n\n[operation_id ${id}${g.replayed ? ", replayed: the saved answer, not charged again" : ""}${g.generationId ? `, generation ${g.generationId}` : ""}]\n`);
|
|
300
|
+
out(`${g.outputText}\n\n[status: ${String(g.response["status"] ?? "unknown")}${reason ? `, reason: ${String(reason)}` : ""}, operation_id ${id}${g.replayed ? ", replayed: the saved answer, not charged again" : ""}${g.generationId ? `, generation ${g.generationId}` : ""}]\n`);
|
|
298
301
|
return 0;
|
|
299
302
|
}
|
|
300
303
|
}
|
package/dist/client.d.ts
CHANGED
|
@@ -35,7 +35,8 @@ export declare class FloviaClient {
|
|
|
35
35
|
usageToday(args?: UsageTodayArgs): Promise<UsageTodayReply>;
|
|
36
36
|
/**
|
|
37
37
|
* POST /openai/v1/responses. operationId is required: save it with the request before calling. Sending the same request with the same id
|
|
38
|
-
* again
|
|
38
|
+
* again can return the saved answer (replayed: true) for about 15 minutes at no charge. Pending, unknown and expired answers
|
|
39
|
+
* are not proof that nothing ran. Never resent automatically, and never under a new id to recover an answer.
|
|
39
40
|
*/
|
|
40
41
|
generate(request: GenerationRequest, options: {
|
|
41
42
|
operationId: string;
|
package/dist/client.js
CHANGED
|
@@ -143,7 +143,8 @@ export class FloviaClient {
|
|
|
143
143
|
}
|
|
144
144
|
/**
|
|
145
145
|
* POST /openai/v1/responses. operationId is required: save it with the request before calling. Sending the same request with the same id
|
|
146
|
-
* again
|
|
146
|
+
* again can return the saved answer (replayed: true) for about 15 minutes at no charge. Pending, unknown and expired answers
|
|
147
|
+
* are not proof that nothing ran. Never resent automatically, and never under a new id to recover an answer.
|
|
147
148
|
*/
|
|
148
149
|
async generate(request, options) {
|
|
149
150
|
const op = options.operationId;
|
package/package.json
CHANGED