@asgard-js/core
v0.3.4
Published
This package contains the core functionalities of the AsgardJs SDK, providing essential tools for interacting with the Asgard AI platform through Server-Sent Events (SSE) and conversation management.
Keywords
Readme
AsgardJs Core
This package contains the core functionalities of the AsgardJs SDK, providing essential tools for interacting with the Asgard AI platform through Server-Sent Events (SSE) and conversation management.
Installation
To install the core package, use the following command:
npm install @asgard-js/core
Usage
Here's a basic example of how to use the core package:
import { AsgardServiceClient, FetchSseAction, EventType } from '@asgard-js/core';
const client = new AsgardServiceClient({
apiKey: 'your-api-key',
botProviderEndpoint: 'https://api.asgard-ai.com/ns/{namespace}/bot-provider/{botProviderId}',
debugMode: true, // Enable to see deprecation warnings
});
// Use the client to send messages via SSE
client.fetchSse({
customChannelId: 'your-channel-id',
text: 'Hello, Asgard!',
action: FetchSseAction.NONE,
});
// Upload files (optional, requires uploadFile method)
if (client.uploadFile) {
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
try {
const uploadResponse = await client.uploadFile(file, 'your-channel-id');
if (uploadResponse.isSuccess && uploadResponse.data[0]) {
const blobId = uploadResponse.data[0].blobId;
// Send message with uploaded file
client.fetchSse({
customChannelId: 'your-channel-id',
text: 'Here is my image:',
action: FetchSseAction.NONE,
blobIds: [blobId],
});
}
} catch (error) {
console.error('File upload failed:', error);
}
}
// Listen to events
client.on(EventType.MESSAGE, response => {
console.log('Received message:', response);
});
client.on(EventType.DONE, response => {
console.log('Conversation completed:', response);
});
client.on(EventType.ERROR, error => {
console.error('Error occurred:', error);
});
Migration from endpoint to botProviderEndpoint
Important: The endpoint configuration option is deprecated. Use botProviderEndpoint instead for simplified configuration.
Before (Deprecated)
const client = new AsgardServiceClient({
apiKey: 'your-api-key',
endpoint: 'https://api.asgard-ai.com/ns/{namespace}/bot-provider/{botProviderId}/message/sse',
botProviderEndpoint: 'https://api.asgard-ai.com/ns/{namespace}/bot-provider/{botProviderId}',
});After (Recommended)
const client = new AsgardServiceClient({
apiKey: 'your-api-key',
botProviderEndpoint: 'https://api.asgard-ai.com/ns/{namespace}/bot-provider/{botProviderId}',
// SSE endpoint is automatically derived as: botProviderEndpoint + '/message/sse'
});Benefits:
- Simplified configuration with single endpoint
- Reduced chance of configuration errors
- Automatic endpoint derivation
Backward Compatibility: Existing code using endpoint will continue to work but may show deprecation warnings when debugMode is enabled.
API Reference
The core package exports three main classes for different levels of abstraction (AsgardServiceClient, Channel, Conversation), an HttpError class with an isHttpError type guard for HTTP failure handling, and authentication types for dynamic API key management:
AsgardServiceClient
The main client class for interacting with the Asgard AI platform.
Constructor Options (ClientConfig)
- apiKey:
string(optional) - API key for authentication. Can be provided later via dynamic authentication - botProviderEndpoint:
string(required) - Bot provider endpoint URL (SSE endpoint will be auto-derived) - endpoint?:
string(deprecated) - Legacy API endpoint URL. UsebotProviderEndpointinstead. - debugMode?:
boolean- Enable debug mode for deprecation warnings, defaults tofalse - transformSsePayload?:
(payload: FetchSsePayload) => FetchSsePayload- SSE payload transformer - customHeaders?:
Record<string, string>- Custom headers to include in SSE and API requests (e.g., Bearer token viaAuthorizationheader) - userIdentityHint?:
string- Optional user identity hint. When provided, all requests will include theX-ASGARD-USER-IDENTITY-HINTheader with this value - onRunInit?:
InitEventHandler- Handler for run initialization events - onMessage?:
MessageEventHandler- Handler for message events - onToolCall?:
ToolCallEventHandler- Handler for tool call events - onProcess?:
ProcessEventHandler- Handler for process events - onRunDone?:
DoneEventHandler- Handler for run completion events - onRunError?:
ErrorEventHandler- Error handler for execution errors
Methods
- fetchSse(payload, options?): Send a message via Server-Sent Events.
payload.actionis aFetchSseActionvalue —NONEfor a normal message,RESET_CHANNELto (re)initialize the channel,RESPONSE_TOOL_CALL_CONSENTto answer a consent prompt - uploadFile(file, customChannelId): Upload file to Blob API and return BlobUploadResponse
- downloadChannelHomeFile(relativePath, customChannelId):
Promise<ChannelHomeDownloadResult>- Download a file from the channel's Channel Home file-exchange plane (backschannel-home://URI actions); resolves to{ blob, filename } - on(event, handler): Listen to a specific SSE event.
eventmust be anEventTypevalue (e.g.EventType.MESSAGE), not a plain string; registering a listener for an event replaces any previous one - detach({ timeoutMs }): Detach from the owning component without aborting in-flight runs — the connection stays open so the backend can finish the current run, then auto-closes once all runs settle (or after
timeoutMsas a safety net). Backs the ReactkeepConnectionOnUnmountprop - close(): Close the SSE connection and clean up resources (idempotent)
Event Types
Pass these EventType members (imported from @asgard-js/core) as the first argument to on():
EventType.INIT(asgard.run.init): Run initialization eventsEventType.MESSAGE(asgard.message): Message events (start, delta, complete)EventType.TOOL_CALL(asgard.tool_call): Tool call events (start, complete)EventType.TOOL_CALL_CONSENT(asgard.tool_call.consent): Tool call consent prompts awaiting a user decisionEventType.PROCESS(asgard.process): Process events (start, complete)EventType.DONE(asgard.run.done): Run completion eventsEventType.ERROR(asgard.run.error): Error events
Channel
Higher-level abstraction for managing a conversation channel with reactive state management using RxJS.
Static Methods
- Channel.reset(config, payload?, options?):
Promise<Channel>- Create and initialize a new channel
Instance Methods
- sendMessage(payload, options?):
Promise<void>- Send a message through the channel - replyToolCallConsents(answers, options?, payload?):
Promise<void>- Reply to a pending tool-call consent prompt.answersis an array ofToolCallConsentAnswer(each{ toolCallId, result, denyReason }, whereresultis aToolCallConsentResultvalue) - close():
void- Close the channel and cleanup subscriptions
Configuration (ChannelConfig)
- client:
IAsgardServiceClient- Instance of AsgardServiceClient - customChannelId:
string- Unique channel identifier - customMessageId?:
string- Optional message ID - conversation:
Conversation- Initial conversation state - statesObserver?:
ObserverOrNext<ChannelStates>- Observer for channel state changes
Properties
- customChannelId:
string- The channel identifier - customMessageId?:
string- Optional message identifier
Example Usage
import { AsgardServiceClient, Channel, Conversation } from '@asgard-js/core';
const client = new AsgardServiceClient({
botProviderEndpoint: 'https://api.example.com/bot-provider/123',
apiKey: 'your-api-key',
});
const conversation = new Conversation({ messages: new Map() });
const channel = await Channel.reset({
client,
customChannelId: 'channel-123',
conversation,
statesObserver: states => {
console.log('Connection status:', states.isConnecting);
console.log('Messages:', Array.from(states.conversation.messages.values()));
},
});
// Send a message
await channel.sendMessage({ text: 'Hello, bot!' });
Conversation
Immutable conversation state manager that handles message updates and SSE event processing.
Constructor
- constructor(options): Initialize conversation with
{ messages: Map<string, ConversationMessage> | null, pendingConsent?: ToolCallConsentEventData | null }
Methods
- pushMessage(message):
Conversation- Add a new message (returns new instance) - onMessage(response):
Conversation- Process an SSE response and update the conversation (returns new instance) - clearPendingConsent():
Conversation- Clear the pending tool-call consent (returns new instance)
Properties
- messages:
Map<string, ConversationMessage> | null- Map of all messages in the conversation - pendingConsent:
ToolCallConsentEventData | null- The tool-call consent prompt currently awaiting a user decision, ornull
Message Types
- ConversationUserMessage: User-sent messages with
textandtime - ConversationBotMessage: Bot responses with
message,isTyping,typingText,eventType - ConversationToolCallMessage: Tool-call entries with
toolName,reason,parameter,result,isComplete - ConversationErrorMessage: Error messages with
errordetails
Example Usage
import { Conversation } from '@asgard-js/core';
// Create new conversation
const conversation = new Conversation({ messages: new Map() });
// Add a user message
const userMessage = {
messageId: 'msg-1',
type: 'user',
text: 'Hello',
time: new Date(),
};
const updatedConversation = conversation.pushMessage(userMessage);
console.log('Messages:', Array.from(updatedConversation.messages.values()));
File Upload API
The core package includes file upload capabilities for sending images through the chatbot.
// Upload file and send message with attachment
const uploadResponse = await client.uploadFile(file, customChannelId);
if (uploadResponse.isSuccess && uploadResponse.data[0]) {
const blobId = uploadResponse.data[0].blobId;
client.fetchSse({
customChannelId: 'your-channel-id',
text: 'Here is my image',
action: FetchSseAction.NONE,
blobIds: [blobId],
});
}Note: uploadFile is optional - check client.uploadFile exists before use. Supports JPEG, PNG, GIF, WebP up to 20MB.
Authentication Types
The core package includes authentication-related types for dynamic API key management:
AuthState
Authentication state management for applications requiring dynamic API key input:
type AuthState =
| 'loading'
| 'needApiKey'
| 'authenticated'
| 'error'
| 'invalidApiKey'
| 'subscriptionExpired'
| 'botNotFound';States:
loading: Authentication in progressneedApiKey: User needs to provide API keyauthenticated: Successfully authenticatederror: General authentication errorinvalidApiKey: API key is invalidsubscriptionExpired: The workspace subscription has expiredbotNotFound: The configured bot provider could not be found
Usage:
import { AuthState } from '@asgard-js/core';
function handleAuthState(state: AuthState) {
switch (state) {
case 'needApiKey':
// Show API key input interface
break;
case 'authenticated':
// Initialize chatbot normally
break;
// Handle other states...
}
}
Error Handling (HttpError)
HTTP failures (for example a non-2xx response while authenticating) are surfaced as an HttpError instance. Both HttpError and the isHttpError type guard are re-exported from the package root:
import { isHttpError } from '@asgard-js/core';
try {
// ... a call that may reject with an HttpError
} catch (error) {
if (isHttpError(error)) {
console.error(error.status, error.statusText, error.body);
}
}HttpError extends Error with readonly status: number, statusText: string, and body: unknown (its name is 'HttpError').
Tool Call Consent
When a bot is configured to ask before running a tool, the backend emits an EventType.TOOL_CALL_CONSENT event. The pending request is exposed on Conversation.pendingConsent; reply to it with Channel.replyToolCallConsents():
import { ToolCallConsentResult } from '@asgard-js/core';
await channel.replyToolCallConsents([
{ toolCallId: 'call-1', result: ToolCallConsentResult.ALLOW_ONCE, denyReason: '' },
]);Related types:
ToolCallConsentResult(enum):ALLOW_ONCE|ALLOW_ALWAYS|DENY_ONCEToolCallConsentPendingCall:{ toolCallId, toolsetName, toolName, parameter, alreadyAllowed, reason? }ToolCallConsentEventData:{ processId, pendingCalls: ToolCallConsentPendingCall[] }ToolCallConsentAnswer:{ toolCallId, result, denyReason }
ChannelHomeDownloadResult
Returned by client.downloadChannelHomeFile():
interface ChannelHomeDownloadResult {
blob: Blob;
filename: string;
}
Development
To develop the core package locally, follow these steps:
Clone the repository and navigate to the project root directory.
Install dependencies:
npm install- Start development:
You can use the following commands to work with the core package:
# Lint the core package
npm run lint:core
# Build the package
npm run build:core
# Watch mode for development
npm run watch:coreSetup your npm registry token for npm publishing:
cd ~/
touch .npmrc
echo "//registry.npmjs.org/:_authToken={{YOUR_TOKEN}}" >> .npmrcFor working with both core and React packages:
# Lint both packages
npm run lint:packages
# Build core package (required for React package)
npm run build:core
npm run build:react
# Release packages
npm run release:core # Release core package
npm run release:react # Release React packageAll builds will be available in the dist directory.
Contributing
We welcome contributions! Please read our contributing guide to get started.
License
This project is licensed under the MIT License - see the LICENSE file for details.
