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

@cosla/sensemaking-tools

v1.1.3

Published

Tools for analyzing large-scale public input using Google's Gemini models. Provides topic identification, statement categorization, and summarization capabilities. Fork of Jigsaw's sensemaking-tools (https://github.com/Jigsaw-Code/sensemaking-tools).

Readme

@cosla/sensemaking-tools

Fork of Jigsaw's sensemaking-tools, maintained by Cosla.

This package is maintained by Cosla as a fork of Jigsaw's original sensemaking-tools. It helps analyze large-scale public input using LLM workflows for topic identification, categorization, and summarization.

Fork and Attribution

@cosla/sensemaking-tools is a maintained fork of Jigsaw's sensemaking-tools.

This package is API/CLI compatible where practical, with fork-specific enhancements documented in this README.

Overview

Effectively understanding large-scale public input is a significant challenge, as traditional methods struggle to translate thousands of diverse opinions into actionable insights. ‘Sensemaker’ showcases how Google's Gemini models can be used to transform massive volumes of raw community feedback into clear, digestible insights, aiding the analysis of these complex discussions.

The tools demonstrated here illustrate methods for:

  • Topic Identification - identifies the main points of discussion. The level of detail is configurable, allowing the tool to discover: just the top level topics; topics and subtopics; or the deepest level — topics, subtopics, and themes (sub-subtopics).
  • Statement Categorization - sorts statements into topics defined by a user or from the Topic Identification function. Statements can belong to more than one topic.
  • Summarization - analyzes statements and vote data to output a summary of the conversation, including an overview, themes discussed, and areas of agreement and disagreement.

These methods were applied in a Jigsaw case study in Bowling Green, Kentucky, analyzing a major U.S. digital civic conversation.

Please see these docs for a full breakdown of available methods and types.

Our Approach

The tools here show how Jigsaw is approaching the application of AI and Google’s Gemini to the emerging field of ‘sensemaking’. It is offered as an insight into our experimental methods. While parts of this library may be adaptable for other projects, developers should anticipate their own work for implementation, customization, and ongoing support for their specific use case.

How It Works

Topic Identification

Sensemaker provides an option to identify the topics present in the comments. The tool offers flexibility to learn:

  • Top-level topics
  • Both top-level and subtopics
  • Sub-topics only, given a set of pre-specified top-level topics

Topic identification code can be found in library/src/tasks/topic_modeling.ts.

Statement Categorization

Categorization assigns statements to one or more of the topics and subtopics. These topics can either be provided by the user, or can be the result of the "topic identification" method described above.

Topics are assigned to statements in batches, asking the model to return the appropriate categories for each statement, and leveraging the Vertex API constrained decoding feature to structure this output according to a pre-specified JSON schema, to avoid issues with output formatting. Additionally, error handling has been added to retry in case an assignment fails.

Statement categorization code can be found in library/src/tasks/categorization.ts.

Summarization

The summarization is output as a narrative report, but users are encouraged to pick and choose which elements are right for their data (see example from the runner here) and consider showing the summarizations alongside visualizations (more tools for this coming soon).

Summarization code can be found in library/rc/tasks/summarization.ts.

Introduction Section

Includes a short bullet list of the number of statements, votes, topics and subtopics within the summary.

Overview Section

The overview section summarizes the "Themes" sections for all subtopics, along with summaries generated for each top-level topic (these summaries are generated as an intermediate step, but not shown to users, and can be thought of as intermediate “chain of thought” steps in the overall recursive summarization approach).

Currently the Overview does not reference the "Common Ground" and "Differences of Opinion" sections described below.

Percentages in the overview (e.g. “Arts and Culture (17%)”) are the percentage of statements that are about this topic. Since statements can be categorized into multiple topics these percentages add up to a number greater than 100%.

Top 5 Subtopics

Sensemaker selects the top 5 subtopics by statement count, and concisely summarizes key themes found in statements within these subtopics. These themes are more concise than what appears later in the summary, to act as a quick overview.

Topic and Subtopic Sections

Using the topics and subtopics from our "Topic Identification" and "Statement Categorization" features, short summaries are produced for each subtopic (or topic, if no subtopics are present).

For each subtopic, Sensemaker surfaces:

  • The number of statements assigned to this subtopic.
  • Prominent themes.
  • A summary of the top statements where we find "common ground" and "differences of opinion", based on agree and disagree rates.
  • The relative level of agreement within the subtopic, as compared to the average subtopic, based on how many comments end up in “common ground” vs “differences of opinion” buckets.

Themes

For each subtopic, Sensemaker identifies up to 5 themes found across statements assigned to that subtopic, and writes a short description of each theme. This section considers all statements assigned to that subtopic.

When identifying themes, Sensemaker leverages statement text and not vote information. Sensemaker attempts to account for differing viewpoints in how it presents themes.

Common Ground and Differences of Opinion

When summarizing "Common Ground" and "Differences of Opinion" within a subtopic, Sensemaker summarizes a sample of statements selected based on statistics calculated using the agree, disagree, and pass vote counts for those statements. For each section, Sensemaker selects statements with the clearest signals for common ground and disagreement, respectively. It does not use any form of text analysis (beyond categorization) when selecting the statements, and only considers vote information.

Because small sample sizes (low vote counts) can create misleading impressions, statements with fewer than 20 votes total are not included. This avoids, for example, a total of 2 votes in favor of a particular statement being taken as evidence of broad support, and included as a point of common ground, when more voting might reveal relatively low support (or significant differences of opinion).

For this section, Sensemaker provides grounding citations to show which statements the LLM referenced, and to allow readers to check the underlying text and vote counts.

Relative Agreement

Each subtopic is labeled as “high”, “moderately high”, “moderately low” or “low” agreement. This is determined by, for each subtopic, getting all the comments that qualify as common ground comments and normalizing it based on how many comments were in that subtopic. Then these numbers are compared subtopic to subtopic.

LLMs Used and Custom Models

This library is implemented using Google Cloud’s VertexAI, and works with the latest Gemini models. The access and quota requirements are controlled by a user’s Google Cloud account.

In addition to Gemini models available through VertexAI, users can integrate custom models using the library’s Model abstraction. This can be done by implementing a class with only two methods, one for generating plain text and one for generating structured data (docs for methods). This allows for the library to be used with models other than Gemini, with other cloud providers, and even with on-premise infrastructure for complete data sovereignty.

Please note that performance results for existing functionality may vary depending on the model selected.

Costs of Running

LLM pricing is based on token count and constantly changing. Here we list the token counts for a conversation with ~1000 statements. Please see Vertex AI pricing for an up-to-date cost per input token. As of April 10, 2025 the cost for running topic identification, statement categorization, and summarization was in total under $1 on Gemini 1.5 Pro.
Token Counts for a 1000 statement conversation

| | Topic Identification | Statement Categorization | Summarization | | ----- | ----- | ----- | ----- | | Input Tokens | 130,000 | 130,000 | 80,000 | | Output Tokens | 50,000 | 50,000 | 7,500 |

Evaluations

Our text summary consists of outputs from multiple LLM calls, each focused on summarizing a subset of comments. We have evaluated these LLM outputs for hallucinations both manually and using autoraters. Autorating code can be found in library/evals/autorating.

We have evaluated topic identification and categorization using methods based on the silhouette coefficient. This evaluation code will be published in the near future. We have also considered how stable the outputs are run to run and comments are categorized into the same topic(s) ~90% of the time, and the identified topics also show high stability.

Running the tools - Setup

First make sure you have npm installed (apt-get install npm on Ubuntu-esque systems).
Next install the project modules by running:
npm install

Using the Default Models - GCloud Authentication

A Google Cloud project is required to control quota and access when using the default models that connect to Model Garden. Installation instructions for all machines are here.
For Linux the GCloud CLI can be installed like:
sudo apt install -y google-cloud-cli
Then to log in locally run:
gcloud config set project <your project name here>
gcloud auth application-default login

Pro Tip: After setting up authentication, run the health check tool first to verify your setup is working correctly:

npx ts-node ./library/runner-cli/health_check_runner.ts --vertexProject <your-project-id> --outputFile health-check

Example Usage - Javascript

Summarize Seattle’s $15 Minimum Wage Conversation.

// Set up the tools to use the default Vertex model (Gemini Pro 1.5) and related authentication info.
const mySensemaker = new Sensemaker({
  defaultModel: new VertexModel(
    "myGoogleCloudProject123,
    "us-central1",
  ),
});

// Note: this function does not exist.
// Get data from a discussion in Seattle over a $15 minimum wage.
// CSV containing comment text, vote counts, and group information from:
// https://github.com/compdemocracy/openData/tree/master/15-per-hour-seattle
const comments: Comments[] = getCommentsFromCsv("./comments.csv");

// Learn what topics were discussed and print them out.
const topics = mySensemaker.learnTopics(
  comments,
  // Should include subtopics:
  true,
  // There are no existing topics:
  undefined,
  // Additional context:
  "This is from a conversation on a $15 minimum wage in Seattle"
);
console.log(topics);

// Summarize the conversation and print the result as Markdown.
const summary = mySensemaker.summarize(
  comments,
  SummarizationType.AGGREGATE_VOTE,
  topics,
  // Additional context:
  "This is from a conversation on a $15 minimum wage in Seattle"
);
console.log(summary.getText("MARKDOWN"));

CLI Usage (Canonical)

This README is the canonical reference for package consumers using the CLI.

There are four CLI tools:

  • ./runner-cli/health_check_runner.ts: health check for your chosen LLM adapter. With Vertex (default), it verifies Google Cloud authentication, Vertex AI connectivity, and model output. With Ollama, it calls GET /api/tags, confirms the requested model is available, and runs a short generate probe. Writes results to the file given by --outputFile.
  • ./runner-cli/runner.ts: takes a CSV representing a conversation and outputs a summary (Markdown/HTML/JSON artifacts).
  • ./runner-cli/categorization_runner.ts: takes a conversation CSV and outputs a CSV with comments categorized into topics/subtopics.
  • ./runner-cli/advanced_runner.ts: writes richer JSON outputs (topics, comments/alignment, summary object) for advanced processing.

These tools process CSV input files. These must contain the columns comment_text and comment-id. For deliberations without group information, vote counts should be set in columns titled agrees, disagrees and passes. If you do not have vote information, these can be set to 0. For deliberations with group breakdowns, you can set the columns {group_name}-agree-count, {group_name}-disagree-count, {group_name}-pass-count.

Run these commands from the repository root using paths like ./library/runner-cli/..., or cd library and use ./runner-cli/....

Quickstart

# 1) Verify setup first (Vertex default)
npx ts-node ./library/runner-cli/health_check_runner.ts \
  --vertexProject <project-name> \
  --outputFile health-check.txt

Shared LLM flags

  • --adapter <vertex|ollama|openai-compatible>: Which API adapter to use. Default: vertex.
  • --provider <openai|openrouter|mistral>: Required when --adapter is openai-compatible.
  • --vertexProject <project>: Google Cloud project id. Required when --adapter is vertex.
  • --vertexLocation <location>: Vertex location/region when --adapter is vertex. Default: global.
  • --baseUrl <url>: Root URL of the provider API. Defaults by adapter/provider:
    • Ollama: http://localhost:11434
    • OpenAI: https://api.openai.com/v1
    • OpenRouter: https://openrouter.ai/api/v1
    • Mistral: https://api.mistral.ai/v1
  • -m, --modelName <model>: Model id for the adapter. Vertex default: gemini-2.5-pro-preview-06-05. Ollama default: gemma3:latest. Required for openai-compatible.
  • --apiKey <token>: API key for openai-compatible. Optional if provider env var is set:
    • OpenAI: OPENAI_API_KEY
    • OpenRouter: OPENROUTER_API_KEY
    • Mistral: MISTRAL_API_KEY
  • -k, --keyFilename <path>: Service account JSON key for Vertex (optional if you use Application Default Credentials).
  • --categorizationBatchSize <n>: Number of statements per categorization batch. Only used when --adapter is ollama (Vertex always uses batch size 100; if you pass this flag with Vertex, it is ignored and a warning is printed).

Concurrency environment variables

The model adapters support environment-variable-based request parallelism controls to avoid rate limiting (for example Tokens Per Minute policies):

  • DEFAULT_PARALLELISM: Shared default parallelism for openai-compatible and ollama model calls.
  • DEFAULT_VERTEX_PARALLELISM: Vertex-specific override for request parallelism.
    • Precedence for Vertex is: DEFAULT_VERTEX_PARALLELISM -> DEFAULT_PARALLELISM -> 2.
    • This keeps Vertex default parallelism at 2 when neither variable is set.

Tool-specific flags

  • health_check_runner: --outputFile (required): path to the report file.
  • categorization_runner: --inputFile, --outputFile; optional --topics, --topicDepth (1-3), --additionalContext, --forceRerun.
  • runner: --inputFile, --outputBasename (prefix for summary.md / .html / .json / etc.); optional --additionalContext.
  • advanced_runner: --inputFile, --outputBasename; optional --additionalContext. Expects comments to already include topics (run categorization first).

Examples:

The examples below assume you have cloned the monorepo (CoslaDigital/sensemaking-tools) and are running commands from the repository root.

If you run commands from the library/ directory instead, remove the ./library prefix from runner paths (for example ./library/runner-cli/health_check_runner.ts becomes ./runner-cli/health_check_runner.ts).

If you run from another project where this package is installed, use ./node_modules/@cosla/sensemaking-tools/runner-cli/... paths.

# Health check — Vertex (default adapter)
npx ts-node ./library/runner-cli/health_check_runner.ts \
  --vertexProject <project-name> \
  --outputFile health-check.txt \
  --keyFilename <key-file-name> \
  --modelName <vertex-model-name>

# Health check — Ollama
npx ts-node ./library/runner-cli/health_check_runner.ts \
  --adapter ollama \
  --baseUrl http://localhost:11434 \
  --modelName gemma3:latest \
  --outputFile health-check-ollama.txt

# Health check — OpenAI-compatible (OpenRouter)
npx ts-node ./library/runner-cli/health_check_runner.ts \
  --adapter openai-compatible \
  --provider openrouter \
  --modelName openai/gpt-5.2 \
  --outputFile health-check-openrouter.txt

# Health check — OpenAI-compatible (Mistral)
npx ts-node ./library/runner-cli/health_check_runner.ts \
  --adapter openai-compatible \
  --provider mistral \
  --modelName mistral-small-latest \
  --outputFile health-check-mistral.txt

# Categorization — Vertex
npx ts-node ./library/runner-cli/categorization_runner.ts \
  --vertexProject <project-name> \
  --inputFile <input.csv> \
  --outputFile <output.csv> \
  --additionalContext "Short description of the conversation"

# Summary runner — Ollama
npx ts-node ./library/runner-cli/runner.ts \
  --adapter ollama \
  --inputFile <input.csv> \
  --outputBasename ./out/run

Troubleshooting

  • ts-node: not found: run npm install in the project using the CLI and ensure npx ts-node --version succeeds.
  • OpenAI-compatible key errors: verify --provider matches the key env var (OPENAI_API_KEY, OPENROUTER_API_KEY, or MISTRAL_API_KEY).
  • 429/rate limit errors: lower parallelism and retry by setting DEFAULT_PARALLELISM (and optionally DEFAULT_VERTEX_PARALLELISM for Vertex).
  • Vertex auth issues: use gcloud auth application-default login or pass --keyFilename with a valid service account JSON.

Generating a Report - Get a webpage presentation of the report

Example of webpage presenting the generated report

To build a webpage presenting the results of the report, follow these steps:

  • Generate the 3 JSON files produced by running the advanced runner CLI tool (mentioned previously).
  • From the command line, access the web app directory by running cd web-ui from the root directory.
  • Then, get the website build (in a folder) by running the following command, being sure to include the paths to each of the 3 JSON files as well as the title of the report. The build will be placed at web-ui > dist > web-ui.
npx ts-node site-build.ts --topics <path-to-topics-file> --summary <path-to-summary-file> --comments <path-to-comments-file> --reportTitle "Title of Report"
  • Access the build (using cd) and start the web server by running npm run dev. Then access the site in a browser at localhost:4200.

Alternatively, a single HTML file for the report can be produced, which can be easily shared.

  • After generating the build, run the following command (from root > web-ui):
npx ts-node single-html-build.js

The tool will output the location of the produced HTML file. Access this file in a browser to view the report.

Making Changes to the tools - Development

Testing

Unit tests can be run with the following command: npm test
To run tests continuously as you make changes run: npm run test-watch

Documentation

The documentation here is the hosted version of the html from the docs/ subdirectory. This documentation is automatically generated using typedoc, and to update the documentation run:
npx typedoc

Contribution and Improvements

This repository offers a transparent view of Jigsaw's approach to large-scale conversation sensemaking with AI. Developers can:

  • Review the code to understand Jigsaw's techniques with LLMs.
  • Leverage components for their own projects (some customization may be needed).
  • Use the command and prompt examples, and overall approach, as inspiration for their own sensemaking tools.

We encourage experimentation and building upon the ideas shared here!

Cloud Vertex Terms of Use

This library is designed to leverage Vertex AI, and usage is subject to the Cloud Vertex Terms of Service and the Generative AI Prohibited Use Policy.