@ykurilov/agent-chat-limits 0.0.0-stage → 0.55.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/CHANGELOG.md +742 -0
- package/LICENSE +21 -0
- package/README.md +180 -2
- package/dist/index.js +841 -0
- package/dist/index.js.map +1 -0
- package/dist/types/create-spending-limits.d.ts +221 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/limit-state.d.ts +16 -0
- package/dist/types/message-windows.d.ts +80 -0
- package/dist/types/spending-period.d.ts +21 -0
- package/dist/types/spending-state.d.ts +11 -0
- package/dist/types/spending-windows.d.ts +68 -0
- package/dist/types/window-reading.d.ts +63 -0
- package/package.json +41 -3
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { SpendingPeriod, SpendingScope } from "@ykurilov/agent-chat-core";
|
|
2
|
+
import type { CalendarUnit } from "./spending-period.js";
|
|
3
|
+
/** Чей лимит считается: человек и чат хода. `chatId` нет — окна чата не читаются. */
|
|
4
|
+
export interface SpendingSubject {
|
|
5
|
+
userId: string;
|
|
6
|
+
chatId?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Лимит окна в долларах или сообщениях: число или функция от человека и чата (у тарифов разные лимиты). Функция
|
|
10
|
+
* зовётся на каждую проверку, так что смена тарифа действует сразу; `undefined` или `null` — у этого человека окна нет.
|
|
11
|
+
*/
|
|
12
|
+
export type WindowLimit = number | ((subject: SpendingSubject) => number | null | undefined | Promise<number | null | undefined>);
|
|
13
|
+
interface WindowBase {
|
|
14
|
+
/** Имя окна: по нему личные лимиты, ручной сброс, события порогов и экран. Уникальное. */
|
|
15
|
+
id: string;
|
|
16
|
+
/** Чей расход: человека (все его чаты), одного чата или всего сервиса (все люди вместе). */
|
|
17
|
+
scope: SpendingScope;
|
|
18
|
+
/** Лимит в долларах. У окна ровно один лимит: `limitUsd` или `limitMessages`. */
|
|
19
|
+
limitUsd?: WindowLimit;
|
|
20
|
+
/**
|
|
21
|
+
* Лимит в сообщениях человека (целое число): считаются отправка и правка, переделка, повтор, служебные и ответы на
|
|
22
|
+
* подтверждения — нет. Счёт ведётся в состоянии лимитов, удаление чата его не сбрасывает; нужны методы состояния
|
|
23
|
+
* хранилища. Ход такого окна не останавливает: место берётся одно на сообщение до его записи.
|
|
24
|
+
*/
|
|
25
|
+
limitMessages?: WindowLimit;
|
|
26
|
+
/** Перерасход окна (ход доделан сверх лимита) переносится в начало следующего окна. По умолчанию нет. Только у денег. */
|
|
27
|
+
carryOverrun?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** Календарное окно: сутки, неделя или месяц по часовому поясу. */
|
|
30
|
+
export interface CalendarWindowConfig extends WindowBase {
|
|
31
|
+
kind: "calendar";
|
|
32
|
+
unit: CalendarUnit;
|
|
33
|
+
/** Пояс IANA; нет — пояс лимитов (`timeZone`), без него — UTC. */
|
|
34
|
+
timeZone?: string;
|
|
35
|
+
/** С какого дня начинается неделя: 1 — понедельник (по умолчанию) … 7 — воскресенье. */
|
|
36
|
+
weekStartsOn?: number;
|
|
37
|
+
}
|
|
38
|
+
/** Скользящее окно: `hours` часов от первого запроса после конца прошлого окна. Нужны методы состояния хранилища. */
|
|
39
|
+
export interface RollingWindowConfig extends WindowBase {
|
|
40
|
+
kind: "rolling";
|
|
41
|
+
hours: number;
|
|
42
|
+
}
|
|
43
|
+
/** Окно на всё время: расход не обнуляется (сбросить можно вручную). */
|
|
44
|
+
export interface TotalWindowConfig extends WindowBase {
|
|
45
|
+
kind: "total";
|
|
46
|
+
}
|
|
47
|
+
export type SpendingWindowConfig = CalendarWindowConfig | RollingWindowConfig | TotalWindowConfig;
|
|
48
|
+
/** Окно после проверки настроек: пояс и день недели подставлены, лимит — всегда функция. */
|
|
49
|
+
export interface SpendingWindow {
|
|
50
|
+
id: string;
|
|
51
|
+
scope: SpendingScope;
|
|
52
|
+
kind: "calendar" | "rolling" | "total";
|
|
53
|
+
unit?: CalendarUnit;
|
|
54
|
+
timeZone: string;
|
|
55
|
+
weekStartsOn: number;
|
|
56
|
+
hours?: number;
|
|
57
|
+
carryOverrun: boolean;
|
|
58
|
+
period: SpendingPeriod;
|
|
59
|
+
/** Чем меряется окно: доллары или сообщения человека. */
|
|
60
|
+
measure: WindowMeasure;
|
|
61
|
+
limit: (subject: SpendingSubject) => Promise<number | undefined>;
|
|
62
|
+
}
|
|
63
|
+
export type WindowMeasure = "usd" | "messages";
|
|
64
|
+
/** Проверяет настройки окон и приводит их к одному виду. Ошибка — при создании лимитов, а не на первом ходе. */
|
|
65
|
+
export declare function normalizeWindows(configs: readonly SpendingWindowConfig[], defaultTimeZone: string | undefined): SpendingWindow[];
|
|
66
|
+
/** Лимит окна: доллары — неотрицательное число, сообщения — целое неотрицательное. */
|
|
67
|
+
export declare function validLimit(amount: unknown, measure: WindowMeasure, where: string): number;
|
|
68
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { SpendingState, UsageFilter } from "@ykurilov/agent-chat-core";
|
|
2
|
+
import type { LimitStateStore } from "./limit-state.js";
|
|
3
|
+
import type { SpendingSubject, SpendingWindow } from "./spending-windows.js";
|
|
4
|
+
/**
|
|
5
|
+
* Состояние окна в хранилище. `start` — начало окна, к которому относится запись (календарное — его начало,
|
|
6
|
+
* скользящее — первый запрос, на всё время — `null`). Перешли в новое окно — перенос, сброс и пороги прошлого не действуют.
|
|
7
|
+
*/
|
|
8
|
+
export interface WindowState {
|
|
9
|
+
start: string | null;
|
|
10
|
+
/** Перерасход прошлого окна, перенесённый в это. */
|
|
11
|
+
carriedUsd: number;
|
|
12
|
+
/** Ручной сброс: расход до этого момента в окне не считается. */
|
|
13
|
+
resetAt?: string;
|
|
14
|
+
/** Пороги, о которых в этом окне уже сообщено. */
|
|
15
|
+
notified: number[];
|
|
16
|
+
}
|
|
17
|
+
export interface WindowReading {
|
|
18
|
+
window: SpendingWindow;
|
|
19
|
+
/** Ключ состояния окна этого человека, чата или сервиса. */
|
|
20
|
+
stateKey: string;
|
|
21
|
+
/** Лимит в долларах; `null` — лимита нет (читается только для отчёта). */
|
|
22
|
+
limitUsd: number | null;
|
|
23
|
+
/** Начало окна; `null` — окно на всё время или скользящее ещё не началось. */
|
|
24
|
+
start: string | null;
|
|
25
|
+
resetsAt: string | null;
|
|
26
|
+
/** Скользящее окно ещё не началось: ждёт первого запроса. */
|
|
27
|
+
waiting: boolean;
|
|
28
|
+
/** Расход в окне вместе с переносом и заказанным, но не записанным. */
|
|
29
|
+
spentUsd: number;
|
|
30
|
+
carriedUsd: number;
|
|
31
|
+
state: SpendingState;
|
|
32
|
+
share: number | null;
|
|
33
|
+
/** Состояние, которое стоит записать: окно сменилось или скользящее открыто этим запросом. */
|
|
34
|
+
nextState: WindowState | null;
|
|
35
|
+
/** Начало окна в прочитанной записи: новая запись ложится, только если её никто не сменил. */
|
|
36
|
+
savedStart: string | null | undefined;
|
|
37
|
+
}
|
|
38
|
+
export interface ReadWindowInput {
|
|
39
|
+
window: SpendingWindow;
|
|
40
|
+
subject: SpendingSubject;
|
|
41
|
+
limitUsd: number | undefined;
|
|
42
|
+
now: Date;
|
|
43
|
+
store: LimitStateStore | null;
|
|
44
|
+
sumSince: (filter: UsageFilter, since: string | null) => Promise<number>;
|
|
45
|
+
warnAt: number;
|
|
46
|
+
/** Скользящее окно, которое не идёт, открывается этим запросом (проверка перед ходом). */
|
|
47
|
+
open: boolean;
|
|
48
|
+
pendingUsd?: number;
|
|
49
|
+
}
|
|
50
|
+
/** Ключ состояния окна; у окна чата без чата — `null` (окно не читается). */
|
|
51
|
+
export declare function windowStateKey(window: SpendingWindow, subject: SpendingSubject): string | null;
|
|
52
|
+
export declare function usageFilterOf(window: SpendingWindow, subject: SpendingSubject): UsageFilter;
|
|
53
|
+
/** Где сейчас окно: начало, конец и идёт ли оно. */
|
|
54
|
+
export declare function currentSpan(window: SpendingWindow, saved: WindowState | null, now: Date, open: boolean): {
|
|
55
|
+
start: null;
|
|
56
|
+
end: null;
|
|
57
|
+
active: boolean;
|
|
58
|
+
} | {
|
|
59
|
+
start: string;
|
|
60
|
+
end: string;
|
|
61
|
+
active: boolean;
|
|
62
|
+
};
|
|
63
|
+
export declare function readWindow(input: ReadWindowInput): Promise<WindowReading | null>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ykurilov/agent-chat-limits",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.55.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md",
|
|
10
|
+
"CHANGELOG.md"
|
|
11
|
+
],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"@ykurilov/agent-chat-source": "./src/index.ts",
|
|
15
|
+
"types": "./dist/types/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"registry": "https://registry.npmjs.org",
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/ykurilov/agent-chat-sdk.git",
|
|
29
|
+
"directory": "packages/limits"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@ykurilov/agent-chat-core": "0.55.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@ai-sdk/provider": "4.0.19",
|
|
36
|
+
"ai": "7.0.122",
|
|
37
|
+
"@ykurilov/agent-chat-server": "0.55.0",
|
|
38
|
+
"@ykurilov/agent-chat-storage-sqlite": "0.55.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "node ../../scripts/build-package.mjs",
|
|
42
|
+
"typecheck": "tsc -p tsconfig.json"
|
|
43
|
+
}
|
|
6
44
|
}
|