mirror of
https://github.com/hanzoai/extension.git
synced 2026-10-31 21:57:06 +00:00
New @hanzo/salesforce SFDX package: a Lightning Web Component assistant on Account/Opportunity/Case/Lead/Contact record pages, backed by Apex callouts to the api.hanzo.ai/v1 gateway (OpenAI-compatible, same wire format as the browser extension, Office add-in, gworkspace add-on, and Slack app). - HanzoClient: pure request/response core (buildMessages/requestBody/ extractContent/parseModels) + ask/listModels callouts via the Hanzo_API Named Credential ā the key never touches Apex. - HanzoContext: SOQL record + related-list context assembly per object, WITH SECURITY_ENFORCED, truncated to a token budget. - HanzoController: @AuraEnabled complete/listModels/writeBack; write-back creates a Task, a ContentNote, or a Chatter FeedItem (as user). - LWC hanzoAssistant: model picker + quick actions (Summarize, Draft email, Next steps, Risks) + prompt + write-back, on record pages. - Named + External Credential for api.hanzo.ai, permission set with principal access, Connected App, Hanzo_Config__mdt default-model config. - Tests: HanzoClientTest + HanzoControllerTest with HttpCalloutMock (asserts URL /v1/chat/completions, bearer, body model+messages, response parse, and each write-back) ā >75% coverage; vitest over the truncate helper. - Registered packages/salesforce in pnpm-workspace.yaml.
34 lines
1.3 KiB
JavaScript
34 lines
1.3 KiB
JavaScript
import { describe, it, expect } from 'vitest';
|
|
import { truncate, MAX_CONTEXT_CHARS } from '../src/context.js';
|
|
|
|
describe('truncate', () => {
|
|
it('passes text within the budget through unchanged', () => {
|
|
expect(truncate('short', 100)).toBe('short');
|
|
expect(truncate('exactly-five', 12)).toBe('exactly-five');
|
|
});
|
|
|
|
it('returns empty string for null/undefined (matches Apex)', () => {
|
|
expect(truncate(null, 100)).toBe('');
|
|
expect(truncate(undefined, 100)).toBe('');
|
|
});
|
|
|
|
it('truncates to exactly maxChars and appends the marker', () => {
|
|
const big = 'x'.repeat(MAX_CONTEXT_CHARS + 500);
|
|
const cut = truncate(big, MAX_CONTEXT_CHARS);
|
|
expect(cut.length).toBe(MAX_CONTEXT_CHARS);
|
|
expect(cut.endsWith('[truncated]')).toBe(true);
|
|
});
|
|
|
|
it('defaults to MAX_CONTEXT_CHARS when no budget given', () => {
|
|
const big = 'y'.repeat(MAX_CONTEXT_CHARS + 1);
|
|
expect(truncate(big).length).toBe(MAX_CONTEXT_CHARS);
|
|
});
|
|
|
|
it('a budget smaller than the marker keeps no head, just the marker (no negative slice)', () => {
|
|
// keep = max(0, 3 - markerLen) = 0, so the result is the marker alone.
|
|
// Both this and Apex HanzoContext.truncate agree: never a negative substring.
|
|
const cut = truncate('abcdefghij', 3);
|
|
expect(cut).toBe('\n⦠[truncated]');
|
|
});
|
|
});
|