npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@centigrade/ai-chat-angular-service

v1.6.2

Published

Angular service that wraps the <ai-chat> Stencil web component. Provides Angular-idiomatic signals and observables for component state, tool-call handling, and full lifecycle management of the Centigrade AI Chat middleware.

Readme

@centigrade/ai-chat-angular-service

An Angular service that integrates the Centigrade AI Chat Widget into your Angular application. It wraps the <ai-chat> web component in an Angular-idiomatic API built on typed signals, observables, and a clean lifecycle model.


⚠️ Prerequisites — Backend Required

This package is the client-side Angular integration layer for the Centigrade AI Chat platform. It requires a dedicated backend middleware deployed and configured by Centigrade for your organization.

Without an active backend deployment you will not be able to establish a connection. The following configuration values are provided by Centigrade when your instance is set up:


Installation

npm install @centigrade/ai-chat-angular-service

Peer dependencies (must already be present in your Angular project):

@angular/common  ^22.0.0
@angular/core    ^22.0.0

What this service enables

Once connected to the Centigrade backend, the <ai-chat> widget gives your users a fully-featured AI chat experience directly inside your Angular app. The service layer on top adds:

  • Angular signals for available, componentState, and isOwned — no async pipes or manual subscriptions needed in templates.
  • Observable-based tool-call handling so your application can respond to AI-initiated function calls (e.g. navigating to a page, fetching live data).
  • Automatic element adoption — if <ai-chat> is already in the DOM (placed in index.html), the service detects and manages it without duplicating the element.
  • Runtime property setters — update the JWT token, language, or any other configuration property reactively after initial mount.
  • Lifecycle safetydestroy() cleanly removes listeners, cancels pending tool calls, and resets all signals.

Quick Start

Option A — Let the service create the element

import { Component, ElementRef, inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { filter } from 'rxjs/operators';
import { AiChatService, CommunicationMode } from '@centigrade/ai-chat-angular-service';

@Component({
  selector: 'app-shell',
  template: `
    <!-- host container for the <ai-chat> element -->
    <div #chatHost></div>

    <!-- reactive state — no async pipe needed -->
    <p>Chat state: {{ chatService.componentState() }}</p>
  `,
})
export class ShellComponent implements OnInit, OnDestroy {
  @ViewChild('chatHost', { static: true }) chatHost!: ElementRef<HTMLDivElement>;

  protected readonly chatService = inject(AiChatService);

  ngOnInit(): void {
    // Mount the widget and load the component script
    this.chatService.inject(
      this.chatHost.nativeElement,
      {
        communicationMode: CommunicationMode.MULTIPLE_ASSISTANT_BASED,
        middlewareBaseUrl: 'https://your-backend.example.com', // provided by Centigrade
        language: 'en_US',
        jwtEnabled: true,
      },
      'https://cdn.example.com/v1/build/ai-chat-component.esm.js', // provided by Centigrade
    );

    // Supply the user's JWT after login
    this.chatService.setToken(myAuthService.getToken());

    // Handle AI-initiated function calls.
    //
    // Option 1a: toolCallFor(name) — single function, null already excluded.
    this.chatService.toolCallFor('navigateTo').subscribe((request) => {
      const { route } = request.dto.functionArgs as { route: string };
      this.router.navigateByUrl(route);
      request.respond(JSON.stringify({ success: true, navigatedTo: route }));
    });

    // Option 1b: toolCallFor([...names]) — handle several functions in one stream.
    this.chatService.toolCallFor(['navigateTo', 'openDialog']).subscribe((request) => {
      const { functionName, functionArgs } = request.dto;
      switch (functionName) {
        case 'navigateTo': {
          const { route } = functionArgs as { route: string };
          this.router.navigateByUrl(route);
          request.respond(JSON.stringify({ success: true, navigatedTo: route }));
          break;
        }
        case 'openDialog': {
          this.dialog.open(functionArgs['dialogId'] as string);
          request.respond(JSON.stringify({ success: true }));
          break;
        }
      }
    });

    // Option 2: toolCall$ with filter(Boolean) — catch-all including a default branch.
    this.chatService.toolCall$.pipe(filter(Boolean)).subscribe((request) => {
      const { functionName, functionArgs } = request.dto;
      switch (functionName) {
        case 'navigateTo': {
          const { route } = functionArgs as { route: string };
          this.router.navigateByUrl(route);
          request.respond(JSON.stringify({ success: true, navigatedTo: route }));
          break;
        }
        case 'getCurrentUser': {
          const user = this.authService.currentUser();
          request.respond(JSON.stringify({ name: user.name, role: user.role }));
          break;
        }
        default:
          request.respond(JSON.stringify({ error: `Unknown function: ${functionName}` }));
      }
    });
  }

  ngOnDestroy(): void {
    this.chatService.destroy();
  }
}

Option B — Element already in index.html

If <ai-chat> is embedded statically in your HTML (as configured in the Centigrade integration guide), the service detects it automatically at startup — no inject() call required for mounting.

ngOnInit(): void {
  // Element already in the DOM — just subscribe to events
  this.chatService.toolCall$.subscribe(request => { /* ... */ });
  this.chatService.setToken(myAuthService.getToken());
}

ngOnDestroy(): void {
  this.chatService.destroy(); // detaches listeners only; element stays in the DOM
}

API Reference

AiChatService

Lifecycle

| Method | Signature | Description | | --------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | inject | (container: HTMLElement, config: AiChatConfig, scriptSrc: string) => void | Creates (or adopts) <ai-chat>, applies configuration, and loads the component script. | | destroy | () => void | Detaches event listeners, cancels pending tool calls, and resets all signals. Removes the element from the DOM only if the service owns it (Option A). |

Signals

| Property | Type | Initial value | Description | | ---------------- | ------------------------ | --------------- | ------------------------------------------------------------ | | available | Signal<boolean> | false | true while an <ai-chat> element is attached and managed. | | componentState | Signal<ComponentState> | 'notInjected' | Current state forwarded from the <ai-chat> element. | | isOwned | Signal<boolean> | false | true when the service created the element (Option A). |

Observables

| Property | Type | Description | | ----------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | toolCall$ | Observable<ToolCallRequest \| null> | Emits each time the AI requests a client-side function call. Emits null after a call is answered or times out. | | componentState$ | Observable<ComponentState> | Observable counterpart to the componentState signal. |

Runtime setters

| Method | Description | | -------------------------------------- | ------------------------------------------------------------------------------- | | setToken(token: string) | Updates the JWT bearer token and triggers a reconnection. | | setUserContext(context: UserContext) | Sets structured user context (role, name, …) forwarded to the AI every request. | | setLanguage(lang: LANG_KEY_KEYS) | Switches the UI language at runtime. | | setProperty(key, value) | Generic setter for any AiChatConfig property. | | toggleChatView(open?: boolean) | Opens or closes the chat window. Omit the argument to toggle. | | answerToolCall(toolCallId, result) | Responds to a specific pending tool call by ID. |

Tool call handling

When the AI backend requests a client-side function call it emits a ToolCallRequest on toolCall$:

interface ToolCallRequest {
  dto: {
    toolCallId: string;
    functionName: string;
    functionArgs: Record<string, unknown>;
  };
  respond(result: string): void; // call this with a JSON-stringified return value
}

If respond() is not called within the configured timeout (toolCallTimeoutMs, default 10 s) the service automatically responds with an error so the conversation can continue.

AiChatConfig

interface AiChatConfig {
  communicationMode: CommunicationMode; // required
  middlewareBaseUrl?: string;
  assistantId?: string;
  sessionId?: string | null;
  language?: LANG_KEY_KEYS; // e.g. 'en_US' | 'de_DE' | …
  jwtEnabled?: boolean;
  jwtToken?: string;
  overwriteChatTitle?: string;
  assistantIcon?: string;
  overwriteInputPlaceholderText?: string;
  overwriteAssistantSelectionTitle?: string;
  overwriteButtonIcon?: string;
  overwriteAvatar?: string;
  useOverwriteIcons?: boolean;
  disableMenuButton?: boolean;
  disableToggleThemeButton?: boolean;
  disableChatToggleButton?: boolean;
  toolCallTimeoutMs?: number; // default: 10 000
  /** Structured user context forwarded to the AI with every request. */
  userContext?: UserContext; // e.g. { userId, name, role, department }
}

ComponentState

type ComponentState =
  | 'notInjected' // before inject() is called
  | 'initializing' // element created, connecting
  | 'jwtTokenNotSet'
  | 'jwtTokenInvalid'
  | 'jwtTokenExpired'
  | 'connecting'
  | 'connected'
  | 'ready' // fully operational
  | 'disconnected'
  | 'notReady'
  | 'error';