Skip to content

Commit

Permalink
knowpro: now also return message matches (#791)
Browse files Browse the repository at this point in the history
* Message selection using knowledge matches
* 3 search methods:
  * searchConversation
  * searchConversationKnowledge
  * searchConversationWithNaturalLanguage 
    * Improvements here
  • Loading branch information
umeshma authored Mar 5, 2025
1 parent 4b50abf commit 9d0739a
Show file tree
Hide file tree
Showing 10 changed files with 304 additions and 97 deletions.
4 changes: 2 additions & 2 deletions ts/examples/chat/src/memory/knowproCommon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export async function matchFilterToConversation(
}
let when: kp.WhenFilter = termFilterToWhenFilter(filter);
when.knowledgeType = knowledgeType;
let searchResults = await kp.searchConversation(
let searchResults = await kp.searchConversationKnowledge(
conversation,
termGroup,
when,
Expand All @@ -42,7 +42,7 @@ export async function matchFilterToConversation(
if (useAnd && (!searchResults || searchResults.size === 0)) {
// Try again with OR
termGroup = termFilterToSearchGroup(filter, false);
searchResults = await kp.searchConversation(
searchResults = await kp.searchConversationKnowledge(
conversation,
termGroup,
when,
Expand Down
55 changes: 31 additions & 24 deletions ts/examples/chat/src/memory/knowproMemory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export async function createKnowproCommands(
cm.timestampMessages(context.podcast.messages, startAt, endAt);

context.conversation = context.podcast;
context.printer.conversation = context.conversation;
context.printer.writeLine("Imported podcast:");
context.printer.writePodcastInfo(context.podcast);
if (!namedArgs.buildIndex) {
Expand Down Expand Up @@ -276,6 +277,7 @@ export async function createKnowproCommands(
},
);
context.conversation = context.images;
context.printer.conversation = context.conversation;
progress.complete();

context.printer.writeLine("Imported images:");
Expand Down Expand Up @@ -415,7 +417,7 @@ export async function createKnowproCommands(

const timer = new StopWatch();
timer.start();
const matches = await kp.searchConversation(
const matches = await kp.searchConversationKnowledge(
conversation,
createSearchGroup(
termArgs,
Expand All @@ -433,7 +435,7 @@ export async function createKnowproCommands(
timer.stop();
if (matches && matches.size > 0) {
context.printer.writeLine();
context.printer.writeSearchResults(
context.printer.writeKnowledgeSearchResults(
conversation,
matches,
namedArgs.maxToDisplay,
Expand Down Expand Up @@ -471,7 +473,7 @@ export async function createKnowproCommands(
const query = namedArgs.query;
const result = await context.knowledgeActions.translateSearchTermsV2(
query,
kp.getTimeRangeSectionForConversation(context.conversation!),
kp.getTimeRangePromptSectionForConversation(context.conversation!),
);
if (!result.success) {
context.printer.writeError(result.message);
Expand All @@ -493,7 +495,7 @@ export async function createKnowproCommands(
},
);
if (searchResults) {
context.printer.writeSearchResults(
context.printer.writeKnowledgeSearchResults(
context.conversation!,
searchResults,
namedArgs.maxToDisplay,
Expand All @@ -508,45 +510,50 @@ export async function createKnowproCommands(
const def = searchDef();
def.description =
"Search using natural language and new knowpro filter";
def.options ??= {};
def.options.showKnowledge = argBool("Show knowledge matches", true);
def.options.showMessages = argBool("Show message matches", false);
return def;
}

commands.kpSearch.metadata = searchDefNew();
async function search(args: string[]): Promise<void> {
if (!ensureConversationLoaded()) {
return;
}
const namedArgs = parseNamedArguments(args, searchDefNew());
const query = namedArgs.query;
const result = await context.searchTranslator.translate(
const result = await kp.searchConversationWithNaturalLanguage(
context.conversation!,
context.searchTranslator,
query,
kp.getTimeRangeSectionForConversation(context.conversation!),
namedArgs.ktype,
{
exactMatch: namedArgs.exact,
},
);
if (!result.success) {
context.printer.writeError(result.message);
return;
}

const filter = result.data;
const [searchResults, filter] = result.data;
if (filter) {
context.printer.writeJson(filter, true);
}
const terms = kp.createSearchGroupFromSearchFilter(filter);
const when = kp.createWhenFromSearchFilter(filter);
const searchResults = await kp.searchConversation(
context.conversation!,
terms,
when,
{
exactMatch: namedArgs.exact,
},
);
if (searchResults) {
context.printer.writeSearchResults(
context.conversation!,
searchResults,
namedArgs.maxToDisplay,
);
if (namedArgs.showKnowledge) {
context.printer.writeKnowledgeSearchResults(
context.conversation!,
searchResults.knowledgeMatches,
namedArgs.maxToDisplay,
);
}
if (namedArgs.showMessages) {
context.printer.writeScoredMessages(
searchResults.messageMatches,
context.conversation!.messages,
namedArgs.maxToDisplay,
);
}
} else {
context.printer.writeLine("No matches");
}
Expand Down
74 changes: 62 additions & 12 deletions ts/examples/chat/src/memory/knowproPrinter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@ export class KnowProPrinter extends ChatPrinter {
super();
}

public writeDateRange(dateTime: kp.DateRange) {
public writeDateRange(dateTime: kp.DateRange): KnowProPrinter {
this.writeLine(`Started: ${dateTime.start}`);
if (dateTime.end) {
this.writeLine(`Ended: ${dateTime.end}`);
}
return this;
}

public writeMetadata(metadata: any) {
public writeMetadata(metadata: any): KnowProPrinter {
this.write("Metadata: ").writeJson(metadata);
return this;
}

public writeMessage(message: kp.IMessage) {
Expand All @@ -42,9 +44,51 @@ export class KnowProPrinter extends ChatPrinter {
return this;
}

public writeScoredMessages(
messageIndexMatches: kp.ScoredMessageIndex[],
messages: kp.IMessage[],
maxToDisplay: number,
) {
this.writeLine(
`Displaying ${maxToDisplay} matches of total ${messages.length}`,
);
if (this.sortAsc) {
this.writeLine(`Sorted in ascending order (lowest first)`);
}
const matchesToDisplay = messageIndexMatches.slice(0, maxToDisplay);
for (let i = 0; i < matchesToDisplay.length; ++i) {
let pos = this.sortAsc ? matchesToDisplay.length - (i + 1) : i;
this.writeScoredMessage(
pos,
matchesToDisplay.length,
matchesToDisplay[pos],
messages,
);
}

return this;
}

private writeScoredMessage(
matchNumber: number,
totalMatches: number,
scoredMessage: kp.ScoredMessageIndex,
messages: kp.IMessage[],
) {
const message = messages[scoredMessage.messageIndex];
this.writeInColor(
chalk.green,
`#${matchNumber + 1} / ${totalMatches}: <${scoredMessage.messageIndex}> [${scoredMessage.score}]`,
);
if (message) {
this.writeMessage(message);
}
this.writeLine();
}

public writeEntity(
entity: knowLib.conversation.ConcreteEntity | undefined,
) {
): KnowProPrinter {
if (entity !== undefined) {
this.writeLine(entity.name.toUpperCase());
this.writeList(entity.type, { type: "csv" });
Expand All @@ -58,14 +102,16 @@ export class KnowProPrinter extends ChatPrinter {
return this;
}

public writeAction(action: knowLib.conversation.Action | undefined) {
public writeAction(
action: knowLib.conversation.Action | undefined,
): KnowProPrinter {
if (action !== undefined) {
this.writeLine(knowLib.conversation.actionToString(action));
}
return this;
}

public writeTopic(topic: kp.Topic | undefined) {
public writeTopic(topic: kp.Topic | undefined): KnowProPrinter {
if (topic !== undefined) {
this.writeLine(topic.text);
}
Expand Down Expand Up @@ -173,9 +219,9 @@ export class KnowProPrinter extends ChatPrinter {
this.writeLine();
}

public writeSearchResult(
public writeSemanticRefSearchResult(
conversation: kp.IConversation,
result: kp.SearchResult | undefined,
result: kp.SemanticRefSearchResult | undefined,
maxToDisplay: number,
) {
if (result) {
Expand All @@ -196,9 +242,9 @@ export class KnowProPrinter extends ChatPrinter {
return this;
}

public writeSearchResults(
public writeKnowledgeSearchResults(
conversation: kp.IConversation,
results: Map<kp.KnowledgeType, kp.SearchResult>,
results: Map<kp.KnowledgeType, kp.SemanticRefSearchResult>,
maxToDisplay: number,
distinct: boolean = false,
) {
Expand Down Expand Up @@ -227,21 +273,25 @@ export class KnowProPrinter extends ChatPrinter {
private writeResult(
conversation: kp.IConversation,
type: kp.KnowledgeType,
results: Map<kp.KnowledgeType, kp.SearchResult>,
results: Map<kp.KnowledgeType, kp.SemanticRefSearchResult>,
maxToDisplay: number,
) {
const result = results.get(type);
if (result !== undefined) {
this.writeTitle(type.toUpperCase());
this.writeSearchResult(conversation, result, maxToDisplay);
this.writeSemanticRefSearchResult(
conversation,
result,
maxToDisplay,
);
}
return this;
}

private writeResultDistinct(
conversation: kp.IConversation,
type: kp.KnowledgeType,
results: Map<kp.KnowledgeType, kp.SearchResult>,
results: Map<kp.KnowledgeType, kp.SemanticRefSearchResult>,
maxToDisplay: number,
) {
if (type !== "topic" && type !== "entity") {
Expand Down
37 changes: 35 additions & 2 deletions ts/packages/knowPro/src/collections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@

import { collections, createTopNList } from "typeagent";
import {
IMessage,
Knowledge,
KnowledgeType,
MessageIndex,
ScoredMessageIndex,
ScoredSemanticRef,
SemanticRef,
SemanticRefIndex,
Expand Down Expand Up @@ -351,7 +352,39 @@ export class SemanticRefAccumulator extends MatchAccumulator<SemanticRefIndex> {
}
}

export class MessageAccumulator extends MatchAccumulator<IMessage> {}
export class MessageAccumulator extends MatchAccumulator<MessageIndex> {
constructor() {
super();
}

public addMessagesForSemanticRef(
semanticRef: SemanticRef,
score: number,
): void {
const messageIndexStart = semanticRef.range.start.messageIndex;
if (semanticRef.range.end) {
const messageIndexEnd = semanticRef.range.end.messageIndex;
for (
let messageIndex = messageIndexStart;
messageIndex < messageIndexEnd;
++messageIndex
) {
this.add(messageIndex, score, true);
}
} else {
this.add(messageIndexStart, score, true);
}
}

public toScoredMessageIndexes(): ScoredMessageIndex[] {
return this.getSortedByScore(0).map((m) => {
return {
messageIndex: m.value,
score: m.score,
};
}, 0);
}
}

export class TextRangeCollection {
// Maintains ranges sorted by message index
Expand Down
19 changes: 19 additions & 0 deletions ts/packages/knowPro/src/conversation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { IConversation, DateRange } from "./interfaces.js";

export function getTimeRangeForConversation(
conversation: IConversation,
): DateRange | undefined {
const messages = conversation.messages;
const start = messages[0].timestamp;
const end = messages[messages.length - 1].timestamp;
if (start !== undefined) {
return {
start: new Date(start),
end: end ? new Date(end) : undefined,
};
}
return undefined;
}
1 change: 1 addition & 0 deletions ts/packages/knowPro/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

export * from "./interfaces.js";
export * from "./import.js";
export * from "./conversation.js";
export * from "./conversationIndex.js";
export * from "./secondaryIndexes.js";
export * from "./relatedTermsIndex.js";
Expand Down
Loading

0 comments on commit 9d0739a

Please sign in to comment.