@modelprofile.com/flexharness 6.1.0 → 7.0.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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts_chat/classes.chatsession.d.ts +37 -0
- package/dist_ts_chat/classes.chatsession.js +102 -0
- package/dist_ts_chat/index.d.ts +2 -0
- package/dist_ts_chat/index.js +2 -0
- package/dist_ts_chat/interfaces.d.ts +72 -0
- package/dist_ts_chat/interfaces.js +2 -0
- package/dist_ts_chat/plugins.d.ts +4 -0
- package/dist_ts_chat/plugins.js +4 -0
- package/dist_ts_chat_cli/components.chatapp.d.ts +10 -0
- package/dist_ts_chat_cli/components.chatapp.js +75 -0
- package/dist_ts_chat_cli/components.inputarea.d.ts +7 -0
- package/dist_ts_chat_cli/components.inputarea.js +18 -0
- package/dist_ts_chat_cli/components.message.d.ts +12 -0
- package/dist_ts_chat_cli/components.message.js +21 -0
- package/dist_ts_chat_cli/components.messagelist.d.ts +9 -0
- package/dist_ts_chat_cli/components.messagelist.js +19 -0
- package/dist_ts_chat_cli/components.statusbar.d.ts +9 -0
- package/dist_ts_chat_cli/components.statusbar.js +15 -0
- package/dist_ts_chat_cli/index.d.ts +2 -0
- package/dist_ts_chat_cli/index.js +2 -0
- package/dist_ts_chat_cli/plugins.d.ts +12 -0
- package/dist_ts_chat_cli/plugins.js +14 -0
- package/dist_ts_chat_cli/startchat.d.ts +18 -0
- package/dist_ts_chat_cli/startchat.js +17 -0
- package/dist_ts_chat_web/flexchat-input.d.ts +21 -0
- package/dist_ts_chat_web/flexchat-input.js +172 -0
- package/dist_ts_chat_web/flexchat-message.d.ts +26 -0
- package/dist_ts_chat_web/flexchat-message.js +174 -0
- package/dist_ts_chat_web/flexchat-window.d.ts +34 -0
- package/dist_ts_chat_web/flexchat-window.js +394 -0
- package/dist_ts_chat_web/index.d.ts +3 -0
- package/dist_ts_chat_web/index.js +4 -0
- package/dist_ts_chat_web/plugins.d.ts +6 -0
- package/dist_ts_chat_web/plugins.js +4 -0
- package/dist_ts_compaction/index.d.ts +2 -0
- package/dist_ts_compaction/index.js +2 -0
- package/dist_ts_compaction/plugins.d.ts +4 -0
- package/dist_ts_compaction/plugins.js +3 -0
- package/dist_ts_compaction/smartagent.compaction.d.ts +14 -0
- package/dist_ts_compaction/smartagent.compaction.js +49 -0
- package/dist_ts_document/index.d.ts +19 -0
- package/dist_ts_document/index.js +55 -0
- package/dist_ts_document/plugins.d.ts +3 -0
- package/dist_ts_document/plugins.js +4 -0
- package/dist_ts_kvm/classes.browserkvm.d.ts +22 -0
- package/dist_ts_kvm/classes.browserkvm.js +271 -0
- package/dist_ts_kvm/classes.kvmterminal.d.ts +8 -0
- package/dist_ts_kvm/classes.kvmterminal.js +88 -0
- package/dist_ts_kvm/commandwrappers.d.ts +14 -0
- package/dist_ts_kvm/commandwrappers.js +58 -0
- package/dist_ts_kvm/index.d.ts +6 -0
- package/dist_ts_kvm/index.js +7 -0
- package/dist_ts_kvm/interfaces.d.ts +106 -0
- package/dist_ts_kvm/interfaces.js +2 -0
- package/dist_ts_kvm/ocr.d.ts +14 -0
- package/dist_ts_kvm/ocr.js +23 -0
- package/dist_ts_kvm/plugins.d.ts +6 -0
- package/dist_ts_kvm/plugins.js +10 -0
- package/dist_ts_kvm/tools.d.ts +15 -0
- package/dist_ts_kvm/tools.js +40 -0
- package/dist_ts_mcp/index.d.ts +86 -0
- package/dist_ts_mcp/index.js +284 -0
- package/dist_ts_mcp/requestcontext.d.ts +11 -0
- package/dist_ts_mcp/requestcontext.js +42 -0
- package/dist_ts_media/index.d.ts +8 -0
- package/dist_ts_media/index.js +21 -0
- package/dist_ts_media/plugins.d.ts +2 -0
- package/dist_ts_media/plugins.js +3 -0
- package/dist_ts_ocr/classes.imageocr.d.ts +19 -0
- package/dist_ts_ocr/classes.imageocr.js +27 -0
- package/dist_ts_ocr/index.d.ts +2 -0
- package/dist_ts_ocr/index.js +3 -0
- package/dist_ts_ocr/ocr.mistral.d.ts +75 -0
- package/dist_ts_ocr/ocr.mistral.js +77 -0
- package/dist_ts_tools/index.d.ts +17 -0
- package/dist_ts_tools/index.js +10 -0
- package/dist_ts_tools/plugins.d.ts +7 -0
- package/dist_ts_tools/plugins.js +6 -0
- package/dist_ts_tools/tool.browser.d.ts +11 -0
- package/dist_ts_tools/tool.browser.js +128 -0
- package/dist_ts_tools/tool.context.d.ts +4 -0
- package/dist_ts_tools/tool.context.js +23 -0
- package/dist_ts_tools/tool.filesystem.d.ts +11 -0
- package/dist_ts_tools/tool.filesystem.js +106 -0
- package/dist_ts_tools/tool.http.d.ts +2 -0
- package/dist_ts_tools/tool.http.js +65 -0
- package/dist_ts_tools/tool.json.d.ts +2 -0
- package/dist_ts_tools/tool.json.js +47 -0
- package/dist_ts_tools/tool.shell.d.ts +15 -0
- package/dist_ts_tools/tool.shell.js +329 -0
- package/dist_ts_tools/utils.truncation.d.ts +10 -0
- package/dist_ts_tools/utils.truncation.js +26 -0
- package/dist_ts_tools_node/index.d.ts +5 -0
- package/dist_ts_tools_node/index.js +4 -0
- package/dist_ts_tools_node/plugins.d.ts +13 -0
- package/dist_ts_tools_node/plugins.js +13 -0
- package/dist_ts_tools_node/tool.context.d.ts +31 -0
- package/dist_ts_tools_node/tool.context.js +837 -0
- package/dist_ts_tools_node/tool.persistence.d.ts +15 -0
- package/dist_ts_tools_node/tool.persistence.js +129 -0
- package/dist_ts_tools_node/tools.local.d.ts +11 -0
- package/dist_ts_tools_node/tools.local.js +15 -0
- package/package.json +112 -4
- package/readme.md +122 -66
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/readme.md +122 -66
- package/ts_chat/classes.chatsession.ts +116 -0
- package/ts_chat/index.ts +8 -0
- package/ts_chat/interfaces.ts +64 -0
- package/ts_chat/plugins.ts +7 -0
- package/ts_chat/readme.md +49 -0
- package/ts_chat_cli/components.chatapp.ts +103 -0
- package/ts_chat_cli/components.inputarea.ts +29 -0
- package/ts_chat_cli/components.message.ts +48 -0
- package/ts_chat_cli/components.messagelist.ts +45 -0
- package/ts_chat_cli/components.statusbar.ts +31 -0
- package/ts_chat_cli/index.ts +2 -0
- package/ts_chat_cli/plugins.ts +21 -0
- package/ts_chat_cli/readme.md +40 -0
- package/ts_chat_cli/startchat.ts +34 -0
- package/ts_chat_web/flexchat-input.ts +184 -0
- package/ts_chat_web/flexchat-message.ts +185 -0
- package/ts_chat_web/flexchat-window.ts +416 -0
- package/ts_chat_web/index.ts +3 -0
- package/ts_chat_web/plugins.ts +9 -0
- package/ts_chat_web/readme.md +46 -0
- package/ts_compaction/index.ts +2 -0
- package/ts_compaction/plugins.ts +6 -0
- package/ts_compaction/readme.md +37 -0
- package/ts_compaction/smartagent.compaction.ts +59 -0
- package/ts_document/index.ts +82 -0
- package/ts_document/plugins.ts +3 -0
- package/ts_document/readme.md +49 -0
- package/ts_kvm/classes.browserkvm.ts +335 -0
- package/ts_kvm/classes.kvmterminal.ts +103 -0
- package/ts_kvm/commandwrappers.ts +83 -0
- package/ts_kvm/index.ts +6 -0
- package/ts_kvm/interfaces.ts +152 -0
- package/ts_kvm/ocr.ts +41 -0
- package/ts_kvm/plugins.ts +13 -0
- package/ts_kvm/readme.md +482 -0
- package/ts_kvm/tools.ts +58 -0
- package/ts_mcp/index.ts +470 -0
- package/ts_mcp/readme.md +37 -0
- package/ts_mcp/requestcontext.ts +66 -0
- package/ts_media/index.ts +29 -0
- package/ts_media/plugins.ts +2 -0
- package/ts_media/readme.md +37 -0
- package/ts_ocr/classes.imageocr.ts +53 -0
- package/ts_ocr/index.ts +2 -0
- package/ts_ocr/ocr.mistral.ts +192 -0
- package/ts_ocr/readme.md +48 -0
- package/ts_tools/index.ts +49 -0
- package/ts_tools/plugins.ts +7 -0
- package/ts_tools/readme.md +37 -0
- package/ts_tools/tool.browser.ts +159 -0
- package/ts_tools/tool.context.ts +23 -0
- package/ts_tools/tool.filesystem.ts +144 -0
- package/ts_tools/tool.http.ts +78 -0
- package/ts_tools/tool.json.ts +53 -0
- package/ts_tools/tool.shell.ts +418 -0
- package/ts_tools/utils.truncation.ts +39 -0
- package/ts_tools_node/index.ts +5 -0
- package/ts_tools_node/plugins.ts +16 -0
- package/ts_tools_node/readme.md +37 -0
- package/ts_tools_node/tool.context.ts +991 -0
- package/ts_tools_node/tool.persistence.ts +146 -0
- package/ts_tools_node/tools.local.ts +26 -0
package/ts/readme.md
CHANGED
|
@@ -1,98 +1,154 @@
|
|
|
1
|
-
FlexHarness is a modular toolbox for model inference,
|
|
1
|
+
FlexHarness is a modular toolbox for model inference, agents, managed sessions, chat, OCR, KVM control and provider capabilities. It brings SmartAI, SmartAgent, SmartChat, SmartKVM and SmartOCR's AI helpers into one repository through four focused packages published with tspublish.
|
|
2
2
|
|
|
3
3
|
## Issue Reporting and Security
|
|
4
4
|
|
|
5
5
|
For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Install and choose an entrypoint
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The toolbox publishes **four packages** under `@modelprofile.com`, with one shared
|
|
10
|
+
release version. Integrations use subpath imports instead of additional package
|
|
11
|
+
names.
|
|
10
12
|
|
|
11
|
-
| Package |
|
|
13
|
+
| Package | Use it for |
|
|
12
14
|
| --- | --- |
|
|
13
|
-
| `flexharness
|
|
14
|
-
| `flexharness-
|
|
15
|
-
| `flexharness-
|
|
16
|
-
| `flexharness` |
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
| `flexharness-media-openai` | OpenAI audio and image capabilities |
|
|
26
|
-
| `flexharness-document` | PDF processing; this component alone brings SmartPDF |
|
|
27
|
-
| `flexharness-ocr` | OCR contracts with an injected transport; no runtime dependencies |
|
|
28
|
-
| `flexharness-research` | Anthropic research capabilities |
|
|
29
|
-
|
|
30
|
-
For model inference without an agent:
|
|
15
|
+
| `flexharness` | Managed sessions, permissions, tools, chat, OCR, documents and KVM |
|
|
16
|
+
| `flexharness-agent` | Standalone `runAgent` and `AgentSession`; portable `/runner` |
|
|
17
|
+
| `flexharness-models` | Model contracts, `ModelRegistry`, AI SDK helpers and caching |
|
|
18
|
+
| `flexharness-providers` | All eight model providers, authentication, accounts, media and research |
|
|
19
|
+
|
|
20
|
+
For a managed agent with model providers:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
pnpm add @modelprofile.com/flexharness @modelprofile.com/flexharness-providers
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
For inference without an agent:
|
|
31
27
|
|
|
32
28
|
```sh
|
|
33
|
-
pnpm add @modelprofile.com/flexharness-models @modelprofile.com/flexharness-
|
|
29
|
+
pnpm add @modelprofile.com/flexharness-models @modelprofile.com/flexharness-providers
|
|
34
30
|
```
|
|
35
31
|
|
|
36
32
|
```typescript
|
|
37
33
|
import { ModelRegistry, generateText } from '@modelprofile.com/flexharness-models';
|
|
38
|
-
import { createOpenAiModelProvider } from '@modelprofile.com/flexharness-
|
|
34
|
+
import { createOpenAiModelProvider } from '@modelprofile.com/flexharness-providers/openai';
|
|
39
35
|
|
|
40
36
|
const models = new ModelRegistry().register(createOpenAiModelProvider());
|
|
41
37
|
const setup = models.getModelSetup({
|
|
42
|
-
provider: 'openai',
|
|
43
|
-
model: 'gpt-5.5',
|
|
44
|
-
apiKey: process.env.OPENAI_API_KEY,
|
|
38
|
+
provider: 'openai', model: 'gpt-5.5', apiKey: process.env.OPENAI_API_KEY,
|
|
45
39
|
});
|
|
46
40
|
const result = await generateText({ ...setup, prompt: 'Hello' });
|
|
47
41
|
```
|
|
48
42
|
|
|
49
|
-
For a standalone agent,
|
|
43
|
+
For a standalone agent, install `@modelprofile.com/flexharness-agent` and pass the
|
|
44
|
+
same setup to `runAgent({ ...setup, prompt: 'Hello' })`. Node.js 24 or newer is
|
|
45
|
+
required for Node entrypoints. Each application owns its model registry; imports
|
|
46
|
+
do not register providers globally.
|
|
47
|
+
|
|
48
|
+
## Harness subpaths
|
|
49
|
+
|
|
50
|
+
All entries below belong to `@modelprofile.com/flexharness`. The root export stays
|
|
51
|
+
focused on managed sessions. Importing it does not load UI, PDF, browser automation
|
|
52
|
+
or provider SDKs.
|
|
53
|
+
|
|
54
|
+
| Import suffix | Capability | Additional install |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| `/tools` | Host-supplied execution contexts, HTTP and JSON tools | None |
|
|
57
|
+
| `/tools/node` | Local filesystem and process tools, file-backed job adapters | None |
|
|
58
|
+
| `/compaction` | Model-driven conversation compaction | None |
|
|
59
|
+
| `/media` | Vision recipes using an injected model | None |
|
|
60
|
+
| `/ocr` | `ImageOcr`, OCR contracts and Mistral transport | None |
|
|
61
|
+
| `/chat` | Portable streaming `ChatSession`, history and usage | None |
|
|
62
|
+
| `/chat/cli` | Ink/React terminal chat | `ink ink-text-input react`; TypeScript: `@types/react` |
|
|
63
|
+
| `/chat/web` | Lit `flexchat-window`, `flexchat-message`, `flexchat-input` | `lit` |
|
|
64
|
+
| `/kvm` | Browser control, terminal framing and OCR observation | `puppeteer` |
|
|
65
|
+
| `/documents` | PDF processing and `extractTextFromPdf` | `@push.rocks/smartpdf` |
|
|
66
|
+
| `/mcp` | MCP clients and AI SDK tool conversion | `@modelcontextprotocol/sdk` |
|
|
67
|
+
| `/migration` | Existing versioned harness migrations | None |
|
|
68
|
+
|
|
69
|
+
The integrations in the last column use optional peer dependencies. Install them
|
|
70
|
+
only for the entrypoints your project imports, for example:
|
|
50
71
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
## Install the managed harness
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
pnpm add @modelprofile.com/flexharness
|
|
72
|
+
```sh
|
|
73
|
+
pnpm add @modelprofile.com/flexharness lit
|
|
57
74
|
```
|
|
58
75
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
76
|
+
```typescript
|
|
77
|
+
import { ChatSession } from '@modelprofile.com/flexharness/chat';
|
|
78
|
+
import '@modelprofile.com/flexharness/chat/web';
|
|
79
|
+
```
|
|
62
80
|
|
|
63
|
-
|
|
81
|
+
Chat uses the portable `flexharness-agent/runner`. A browser application can provide
|
|
82
|
+
a model backed by its own server transport, or supply `run` in
|
|
83
|
+
`IChatSessionOptions`. The web element accepts an `IChatSession` and maintains its
|
|
84
|
+
own subscription. Full Node `AgentSession` remains at the agent package root.
|
|
64
85
|
|
|
65
|
-
##
|
|
86
|
+
## Provider subpaths
|
|
66
87
|
|
|
67
|
-
|
|
88
|
+
Install `@modelprofile.com/flexharness-providers` once. Its root exports all provider
|
|
89
|
+
factories; use a provider subpath to import just that implementation.
|
|
68
90
|
|
|
69
|
-
|
|
|
91
|
+
| Import suffix | Capability |
|
|
70
92
|
| --- | --- |
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
| `
|
|
75
|
-
|
|
|
76
|
-
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
| `/anthropic`, `/openai`, `/google`, `/groq`, `/mistral`, `/xai`, `/perplexity`, `/ollama` | Model adapters and their request types |
|
|
94
|
+
| `/auth` | ChatGPT browser/device authentication, refresh and model connections |
|
|
95
|
+
| `/accounts` | Account lifecycle, model catalogs, rate limits and credential envelopes |
|
|
96
|
+
| `/auth/files` | Explicit interoperability with external credential files |
|
|
97
|
+
| `/media` | OpenAI audio and image generation |
|
|
98
|
+
| `/research` | Anthropic research utilities |
|
|
99
|
+
|
|
100
|
+
Provider SDKs are installed together. Authentication, file access and vendor media
|
|
101
|
+
are separate entrypoints and are not imported by the provider factory root. For
|
|
102
|
+
ChatGPT authentication, pass
|
|
103
|
+
`connection: createOpenAiChatGptModelConnection(credentials)` from `/auth` to the
|
|
104
|
+
OpenAI model options. API-key inference needs no authentication setup.
|
|
105
|
+
|
|
106
|
+
## Migration
|
|
107
|
+
|
|
108
|
+
Replace the withdrawn 6.x component imports with the subpaths above. Provider
|
|
109
|
+
imports change from individual provider packages to
|
|
110
|
+
`@modelprofile.com/flexharness-providers/<provider>`. Keep direct agent and models
|
|
111
|
+
imports and upgrade them to the same release version. The removed package names
|
|
112
|
+
are not republished.
|
|
113
|
+
|
|
114
|
+
| Previous API | Replacement |
|
|
115
|
+
| --- | --- |
|
|
116
|
+
| SmartAI models, caching and AI SDK contracts | `flexharness-models` and explicit `ModelRegistry` registrations |
|
|
117
|
+
| SmartAI provider factories | `flexharness-providers/<provider>` |
|
|
118
|
+
| SmartAI authentication, account adapters and external credential files | `flexharness-providers/auth`, `/accounts`, `/auth/files` |
|
|
119
|
+
| SmartAI vision, document and OCR helpers | `flexharness/media`, `/documents`, `/ocr` |
|
|
120
|
+
| SmartAI audio, image and research helpers | `flexharness-providers/media`, `/research` |
|
|
121
|
+
| SmartAgent runtime, events and persistence contracts | `flexharness-agent` |
|
|
122
|
+
| SmartAgent tools, local tools, compaction and MCP | `flexharness/tools`, `/tools/node`, `/compaction`, `/mcp` |
|
|
123
|
+
| SmartChat sessions, CLI and web components | `flexharness/chat`, `/chat/cli`, `/chat/web` |
|
|
124
|
+
| SmartKVM | `flexharness/kvm`: `BrowserKvm`, `KvmTerminal`, `createKvmTools` |
|
|
125
|
+
| SmartOCR image AI helper | `ImageOcr.recognizeImageBytes` from `flexharness/ocr` |
|
|
126
|
+
| SmartOCR PDF AI helper | `extractTextFromPdf` from `flexharness/documents` |
|
|
127
|
+
|
|
128
|
+
For image OCR, rename `smartAiOcrEngine` to `engine` and `mistralOcrOptions` to
|
|
129
|
+
`mistralOptions`; pass the API key explicitly. Native searchable-PDF processing
|
|
130
|
+
through `SmartOcr.processPdfBuffer` remains in SmartOCR.
|
|
131
|
+
|
|
132
|
+
Existing `ISmartAi*` and `TSmartAi*` contracts, durable event/job schemas, harness
|
|
133
|
+
projections, credential envelopes and external credential paths remain unchanged.
|
|
134
|
+
The OpenAI account `SmartAiProviderRegistry` is separate from inference's
|
|
135
|
+
`ModelRegistry`. The package consolidation requires no data migration.
|
|
136
|
+
|
|
137
|
+
## Developing and releasing
|
|
138
|
+
|
|
139
|
+
Four named `tspublish.json` descriptors own the published packages. Other folder
|
|
140
|
+
descriptors specify compilation order only. Each package explicitly declares its
|
|
141
|
+
owned folders, exports and required or optional peer dependencies. Source and
|
|
142
|
+
compiled declaration mappings support development without workspace links.
|
|
143
|
+
|
|
144
|
+
Run `pnpm build`, `pnpm test` and `pnpm run check:test`. Tests install packed
|
|
145
|
+
artifacts in disposable directories to check minimal dependency graphs, every
|
|
146
|
+
public subpath and browser imports. Live provider and browser authentication tests
|
|
147
|
+
remain opt-in in `test_integration/`.
|
|
148
|
+
|
|
149
|
+
The configured GitZone release prepares four packages, records their exact
|
|
150
|
+
artifacts, and publishes models before its dependents to npmjs and Verdaccio.
|
|
151
|
+
Third-party OpenAI Codex notices accompany the providers package.
|
|
96
152
|
|
|
97
153
|
## Core Setup
|
|
98
154
|
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { runAgent } from './plugins.js';
|
|
2
|
+
import type { ModelMessage, IAgentRunResult } from './plugins.js';
|
|
3
|
+
import type { IChatSessionOptions, IChatCallbacks, IChatUsage } from './interfaces.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ChatSession manages in-memory conversation state and wraps `runAgent()`.
|
|
7
|
+
* Conversation state stays in memory; consumers own persistence.
|
|
8
|
+
*/
|
|
9
|
+
export class ChatSession {
|
|
10
|
+
private messages: ModelMessage[] = [];
|
|
11
|
+
private usage: IChatUsage = {
|
|
12
|
+
inputTokens: 0,
|
|
13
|
+
outputTokens: 0,
|
|
14
|
+
totalTokens: 0,
|
|
15
|
+
turns: 0,
|
|
16
|
+
};
|
|
17
|
+
private busy = false;
|
|
18
|
+
private readonly subscribers = new Set<IChatCallbacks>();
|
|
19
|
+
|
|
20
|
+
constructor(
|
|
21
|
+
private options: IChatSessionOptions,
|
|
22
|
+
private callbacks: IChatCallbacks = {},
|
|
23
|
+
) {}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Send a user message, run the agent, and return the result.
|
|
27
|
+
* Updates internal message history automatically.
|
|
28
|
+
*/
|
|
29
|
+
async send(userMessage: string): Promise<IAgentRunResult> {
|
|
30
|
+
if (this.busy) {
|
|
31
|
+
throw new Error('ChatSession is busy — wait for the current turn to complete.');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this.busy = true;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const result = await (this.options.run ?? runAgent)({
|
|
38
|
+
model: this.options.model,
|
|
39
|
+
system: this.options.system,
|
|
40
|
+
prompt: userMessage,
|
|
41
|
+
tools: this.options.tools,
|
|
42
|
+
maxSteps: this.options.maxSteps ?? 20,
|
|
43
|
+
messages: this.messages,
|
|
44
|
+
onToken: (delta) => this.notify((callbacks) => callbacks.onToken?.(delta)),
|
|
45
|
+
onToolCall: (name, input) => this.notify((callbacks) => callbacks.onToolCall?.(name, input)),
|
|
46
|
+
onToolResult: (name, result) => this.notify((callbacks) => callbacks.onToolResult?.(name, result)),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Update conversation history
|
|
50
|
+
this.messages = result.messages;
|
|
51
|
+
|
|
52
|
+
// Accumulate usage
|
|
53
|
+
this.usage.inputTokens += result.usage.inputTokens;
|
|
54
|
+
this.usage.outputTokens += result.usage.outputTokens;
|
|
55
|
+
this.usage.totalTokens += result.usage.totalTokens;
|
|
56
|
+
this.usage.turns++;
|
|
57
|
+
|
|
58
|
+
this.notify((callbacks) => callbacks.onTurnComplete?.(result));
|
|
59
|
+
|
|
60
|
+
return result;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
63
|
+
this.notify((callbacks) => callbacks.onError?.(err));
|
|
64
|
+
throw err;
|
|
65
|
+
} finally {
|
|
66
|
+
this.busy = false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Whether a turn is currently in progress */
|
|
71
|
+
isBusy(): boolean {
|
|
72
|
+
return this.busy;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Get current conversation history */
|
|
76
|
+
getMessages(): ModelMessage[] {
|
|
77
|
+
return [...this.messages];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Replace messages (e.g. for restoring a saved session) */
|
|
81
|
+
setMessages(messages: ModelMessage[]): void {
|
|
82
|
+
this.messages = [...messages];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Clear conversation history and reset usage */
|
|
86
|
+
clear(): void {
|
|
87
|
+
this.messages = [];
|
|
88
|
+
this.usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0, turns: 0 };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Get accumulated token usage across all turns */
|
|
92
|
+
getUsage(): IChatUsage {
|
|
93
|
+
return { ...this.usage };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Update session options (model, system prompt, tools, etc.) */
|
|
97
|
+
updateOptions(options: Partial<IChatSessionOptions>): void {
|
|
98
|
+
Object.assign(this.options, options);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Update callbacks */
|
|
102
|
+
updateCallbacks(callbacks: Partial<IChatCallbacks>): void {
|
|
103
|
+
Object.assign(this.callbacks, callbacks);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Attach an independent UI observer without replacing the caller's callbacks. */
|
|
107
|
+
subscribe(callbacks: IChatCallbacks): () => void {
|
|
108
|
+
this.subscribers.add(callbacks);
|
|
109
|
+
return () => { this.subscribers.delete(callbacks); };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private notify(invoke: (callbacks: IChatCallbacks) => void): void {
|
|
113
|
+
invoke(this.callbacks);
|
|
114
|
+
for (const callbacks of [...this.subscribers]) invoke(callbacks);
|
|
115
|
+
}
|
|
116
|
+
}
|
package/ts_chat/index.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { TSmartAiLanguageModel, ModelMessage, ToolSet, IAgentRunResult, IAgentRunOptions } from './plugins.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Options for creating a ChatSession.
|
|
5
|
+
*/
|
|
6
|
+
export interface IChatSessionOptions {
|
|
7
|
+
/** The language model to use */
|
|
8
|
+
model: TSmartAiLanguageModel;
|
|
9
|
+
/** System prompt for the agent */
|
|
10
|
+
system?: string;
|
|
11
|
+
/** Tools available to the agent */
|
|
12
|
+
tools?: ToolSet;
|
|
13
|
+
/** Maximum agentic steps per turn */
|
|
14
|
+
maxSteps?: number;
|
|
15
|
+
/** Optional caller-owned runner, for example a server transport. */
|
|
16
|
+
run?: (options: IAgentRunOptions) => Promise<IAgentRunResult>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Structural session contract shared by local sessions and remote frontends. */
|
|
20
|
+
export interface IChatSession {
|
|
21
|
+
send(userMessage: string): Promise<IAgentRunResult>;
|
|
22
|
+
isBusy(): boolean;
|
|
23
|
+
getMessages(): ModelMessage[];
|
|
24
|
+
getUsage(): IChatUsage;
|
|
25
|
+
updateCallbacks(callbacks: Partial<IChatCallbacks>): void;
|
|
26
|
+
subscribe(callbacks: IChatCallbacks): () => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Callbacks for real-time streaming events during a turn.
|
|
31
|
+
*/
|
|
32
|
+
export interface IChatCallbacks {
|
|
33
|
+
/** Called for each streamed text token */
|
|
34
|
+
onToken?: (delta: string) => void;
|
|
35
|
+
/** Called when a tool call starts */
|
|
36
|
+
onToolCall?: (name: string, input: unknown) => void;
|
|
37
|
+
/** Called when a tool call completes */
|
|
38
|
+
onToolResult?: (name: string, result: unknown) => void;
|
|
39
|
+
/** Called when a turn completes */
|
|
40
|
+
onTurnComplete?: (result: IAgentRunResult) => void;
|
|
41
|
+
/** Called on error */
|
|
42
|
+
onError?: (error: Error) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Events emitted during chat.
|
|
47
|
+
*/
|
|
48
|
+
export type TChatEvent =
|
|
49
|
+
| { type: 'token'; delta: string }
|
|
50
|
+
| { type: 'tool_call_start'; name: string; input: unknown }
|
|
51
|
+
| { type: 'tool_call_end'; name: string; result: string }
|
|
52
|
+
| { type: 'turn_start' }
|
|
53
|
+
| { type: 'turn_complete'; result: IAgentRunResult }
|
|
54
|
+
| { type: 'error'; error: Error };
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Usage stats accumulated across all turns.
|
|
58
|
+
*/
|
|
59
|
+
export interface IChatUsage {
|
|
60
|
+
inputTokens: number;
|
|
61
|
+
outputTokens: number;
|
|
62
|
+
totalTokens: number;
|
|
63
|
+
turns: number;
|
|
64
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// @modelprofile.com/flexharness-agent
|
|
2
|
+
import { runAgent } from '@modelprofile.com/flexharness-agent/runner';
|
|
3
|
+
export { runAgent };
|
|
4
|
+
export type { IAgentRunOptions, IAgentRunResult } from '@modelprofile.com/flexharness-agent/runner';
|
|
5
|
+
|
|
6
|
+
// @modelprofile.com/flexharness-models (types re-exported from ai SDK)
|
|
7
|
+
export type { TSmartAiLanguageModel, ModelMessage, ToolSet } from '@modelprofile.com/flexharness-models';
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
In-memory chat sessions with streamed text, tool callbacks, conversation history and token usage. Works in Node and modern secure browser contexts.
|
|
2
|
+
|
|
3
|
+
```sh
|
|
4
|
+
pnpm add @modelprofile.com/flexharness
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { ChatSession } from '@modelprofile.com/flexharness/chat';
|
|
9
|
+
|
|
10
|
+
const chat = new ChatSession({ model, system: 'Answer concisely.' }, {
|
|
11
|
+
onToken: (text) => console.log(text),
|
|
12
|
+
onToolResult: (name, output) => console.log(name, output),
|
|
13
|
+
});
|
|
14
|
+
await chat.send('Hello');
|
|
15
|
+
console.log(chat.getMessages(), chat.getUsage());
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Supply a model from your selected FlexHarness provider or a model backed by your server transport. Provider SDKs, terminal UI and web UI are separate installs. Private provider credentials belong on the server.
|
|
19
|
+
|
|
20
|
+
`ChatSession` accepts `model`, `system`, `tools`, `maxSteps` and optional `run`. Its default runner is `flexharness-agent/runner`, which owns cleanup internally and preserves the agent's retry and tool ledger behavior. Pass a caller-owned `run` function to use another execution transport.
|
|
21
|
+
|
|
22
|
+
Use `setMessages` to restore history, `clear` to reset history and usage, and `updateOptions` to change future turns. Concurrent `send` calls reject while a turn is active. Persistence is caller-owned. `updateCallbacks` updates the primary callbacks; `subscribe` adds an independent observer and returns its unsubscribe function. UI adapters use the structural `IChatSession` interface.
|
|
23
|
+
|
|
24
|
+
This replaces `@push.rocks/smartchat`. Select `flexharness/chat/cli` for the terminal or `flexharness/chat/web` for Lit elements.
|
|
25
|
+
|
|
26
|
+
## Issue Reporting and Security
|
|
27
|
+
|
|
28
|
+
Report issues and security concerns through [community.foss.global](https://community.foss.global).
|
|
29
|
+
|
|
30
|
+
## License and Legal Information
|
|
31
|
+
|
|
32
|
+
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the repository license file.
|
|
33
|
+
|
|
34
|
+
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
|
35
|
+
|
|
36
|
+
### Trademarks
|
|
37
|
+
|
|
38
|
+
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
|
|
39
|
+
|
|
40
|
+
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
|
|
41
|
+
|
|
42
|
+
### Company Information
|
|
43
|
+
|
|
44
|
+
Task Venture Capital GmbH<br>
|
|
45
|
+
Registered at District Court Bremen HRB 35230 HB, Germany
|
|
46
|
+
|
|
47
|
+
For any legal inquiries or further information, please contact us via email at hello@task.vc.
|
|
48
|
+
|
|
49
|
+
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { React, h, Box, Text, useInput, useApp, ChatSession } from './plugins.js';
|
|
2
|
+
import type { TSmartAiLanguageModel, ToolSet, IChatUsage } from './plugins.js';
|
|
3
|
+
import { MessageList } from './components.messagelist.js';
|
|
4
|
+
import { InputArea } from './components.inputarea.js';
|
|
5
|
+
import { StatusBar } from './components.statusbar.js';
|
|
6
|
+
import type { IChatMessage } from './components.message.js';
|
|
7
|
+
|
|
8
|
+
export interface IChatAppProps {
|
|
9
|
+
model: TSmartAiLanguageModel;
|
|
10
|
+
system?: string;
|
|
11
|
+
tools?: ToolSet;
|
|
12
|
+
maxSteps?: number;
|
|
13
|
+
modelName?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const emptyUsage: IChatUsage = { inputTokens: 0, outputTokens: 0, totalTokens: 0, turns: 0 };
|
|
17
|
+
|
|
18
|
+
export function ChatApp({ model, system, tools, maxSteps, modelName }: IChatAppProps): React.ReactElement {
|
|
19
|
+
const { exit } = useApp();
|
|
20
|
+
const [messages, setMessages] = React.useState<IChatMessage[]>([]);
|
|
21
|
+
const [streamingText, setStreamingText] = React.useState('');
|
|
22
|
+
const [busy, setBusy] = React.useState(false);
|
|
23
|
+
const [usage, setUsage] = React.useState<IChatUsage>({ ...emptyUsage });
|
|
24
|
+
|
|
25
|
+
const sessionRef = React.useRef<ChatSession | null>(null);
|
|
26
|
+
|
|
27
|
+
if (!sessionRef.current) {
|
|
28
|
+
sessionRef.current = new ChatSession(
|
|
29
|
+
{ model, system, tools, maxSteps: maxSteps ?? 20 },
|
|
30
|
+
{
|
|
31
|
+
onToken: (delta) => {
|
|
32
|
+
setStreamingText((prev) => prev + delta);
|
|
33
|
+
},
|
|
34
|
+
onToolCall: (name, input) => {
|
|
35
|
+
setMessages((prev) => [
|
|
36
|
+
...prev,
|
|
37
|
+
{
|
|
38
|
+
role: 'tool' as const,
|
|
39
|
+
content: '',
|
|
40
|
+
toolName: name,
|
|
41
|
+
toolInput: typeof input === 'string' ? input : JSON.stringify(input),
|
|
42
|
+
},
|
|
43
|
+
]);
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
useInput((input, key) => {
|
|
50
|
+
if (input === 'c' && key.ctrl) {
|
|
51
|
+
exit();
|
|
52
|
+
}
|
|
53
|
+
if (input === 'l' && key.ctrl) {
|
|
54
|
+
setMessages([]);
|
|
55
|
+
setStreamingText('');
|
|
56
|
+
sessionRef.current?.clear();
|
|
57
|
+
setUsage({ ...emptyUsage });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const handleSubmit = async (text: string) => {
|
|
62
|
+
if (busy) return;
|
|
63
|
+
|
|
64
|
+
if (text === '/clear') {
|
|
65
|
+
setMessages([]);
|
|
66
|
+
setStreamingText('');
|
|
67
|
+
sessionRef.current?.clear();
|
|
68
|
+
setUsage({ ...emptyUsage });
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (text === '/quit' || text === '/exit') {
|
|
73
|
+
exit();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
setMessages((prev) => [...prev, { role: 'user', content: text }]);
|
|
78
|
+
setBusy(true);
|
|
79
|
+
setStreamingText('');
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const result = await sessionRef.current!.send(text);
|
|
83
|
+
setStreamingText('');
|
|
84
|
+
setMessages((prev) => [...prev, { role: 'assistant', content: result.text }]);
|
|
85
|
+
setUsage(sessionRef.current!.getUsage());
|
|
86
|
+
} catch (error) {
|
|
87
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
88
|
+
setMessages((prev) => [...prev, { role: 'assistant', content: `Error: ${errMsg}` }]);
|
|
89
|
+
} finally {
|
|
90
|
+
setBusy(false);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
return h(Box, { flexDirection: 'column' as const },
|
|
95
|
+
h(Box, { paddingX: 1 },
|
|
96
|
+
h(Text, { bold: true, color: 'cyan' }, 'FlexChat'),
|
|
97
|
+
modelName ? h(Text, { dimColor: true }, ` \u2014 ${modelName}`) : null,
|
|
98
|
+
),
|
|
99
|
+
h(MessageList, { messages, streamingText, busy }),
|
|
100
|
+
h(InputArea, { disabled: busy, onSubmit: handleSubmit }),
|
|
101
|
+
h(StatusBar, { modelName, usage, busy }),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { React, h, Box, Text, TextInput } from './plugins.js';
|
|
2
|
+
|
|
3
|
+
interface IInputAreaProps {
|
|
4
|
+
disabled: boolean;
|
|
5
|
+
onSubmit: (text: string) => void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function InputArea({ disabled, onSubmit }: IInputAreaProps): React.ReactElement {
|
|
9
|
+
const [value, setValue] = React.useState('');
|
|
10
|
+
|
|
11
|
+
const handleSubmit = (text: string) => {
|
|
12
|
+
if (!text.trim()) return;
|
|
13
|
+
onSubmit(text.trim());
|
|
14
|
+
setValue('');
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
return h(
|
|
18
|
+
Box,
|
|
19
|
+
{ borderStyle: 'single' as const, borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, paddingX: 1 },
|
|
20
|
+
h(Text, { bold: true, color: disabled ? 'gray' : 'cyan' }, '> '),
|
|
21
|
+
h(TextInput, {
|
|
22
|
+
value,
|
|
23
|
+
onChange: setValue,
|
|
24
|
+
onSubmit: handleSubmit,
|
|
25
|
+
placeholder: disabled ? 'Waiting for response...' : 'Type a message...',
|
|
26
|
+
focus: !disabled,
|
|
27
|
+
}),
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { React, h, Box, Text } from './plugins.js';
|
|
2
|
+
|
|
3
|
+
export interface IChatMessage {
|
|
4
|
+
role: 'user' | 'assistant' | 'tool';
|
|
5
|
+
content: string;
|
|
6
|
+
toolName?: string;
|
|
7
|
+
toolInput?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface IMessageProps {
|
|
11
|
+
message: IChatMessage;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function truncate(text: string, maxLen: number): string {
|
|
15
|
+
if (text.length <= maxLen) return text;
|
|
16
|
+
return text.substring(0, maxLen) + '...';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function Message({ message }: IMessageProps): React.ReactElement | null {
|
|
20
|
+
switch (message.role) {
|
|
21
|
+
case 'user':
|
|
22
|
+
return h(Box, { marginY: 0 },
|
|
23
|
+
h(Text, { bold: true, color: 'blue' }, 'You: '),
|
|
24
|
+
h(Text, null, message.content),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
case 'assistant':
|
|
28
|
+
return h(Box, { marginY: 0 },
|
|
29
|
+
h(Text, { bold: true, color: 'green' }, 'Assistant: '),
|
|
30
|
+
h(Text, null, message.content),
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
case 'tool':
|
|
34
|
+
return h(Box, { marginY: 0, marginLeft: 2, flexDirection: 'column' as const },
|
|
35
|
+
h(Text, { dimColor: true },
|
|
36
|
+
`tool: ${message.toolName}${message.toolInput ? ` (${message.toolInput})` : ''}`,
|
|
37
|
+
),
|
|
38
|
+
message.content
|
|
39
|
+
? h(Box, { marginLeft: 2 },
|
|
40
|
+
h(Text, { dimColor: true }, truncate(message.content, 200)),
|
|
41
|
+
)
|
|
42
|
+
: null,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
default:
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|