aboutsummaryrefslogtreecommitdiff
path: root/packages/tools/src/vercel/util.ts
blob: 01b655e1a8f3ebf2d06652e4bdbd8ffa0109086c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import type {
	LanguageModelV2,
	LanguageModelV2CallOptions,
	LanguageModelV2Message,
	LanguageModelV2StreamPart,
	LanguageModelV3,
	LanguageModelV3CallOptions,
	LanguageModelV3Message,
	LanguageModelV3StreamPart,
} from "@ai-sdk/provider"

// Union types for dual SDK version support (V2 = SDK 5, V3 = SDK 6)
export type LanguageModel = LanguageModelV2 | LanguageModelV3
export type LanguageModelCallOptions =
	| LanguageModelV2CallOptions
	| LanguageModelV3CallOptions
export type LanguageModelMessage =
	| LanguageModelV2Message
	| LanguageModelV3Message
export type LanguageModelStreamPart =
	| LanguageModelV2StreamPart
	| LanguageModelV3StreamPart

/**
 * Response structure from the Supermemory profile API.
 */
export interface ProfileStructure {
	profile: {
		/**
		 * Core, stable facts about the user that rarely change.
		 * Examples: name, profession, long-term preferences, goals.
		 */
		static?: Array<{ memory: string; metadata?: Record<string, unknown> }>
		/**
		 * Recently learned or frequently updated information about the user.
		 * Examples: current projects, recent interests, ongoing topics.
		 */
		dynamic?: Array<{ memory: string; metadata?: Record<string, unknown> }>
	}
	searchResults: {
		/**
		 * Memories retrieved based on semantic similarity to the current query.
		 * Most relevant to the immediate conversation context.
		 */
		results: Array<{ memory: string; metadata?: Record<string, unknown> }>
	}
}

/**
 * Simplified profile data for markdown conversion.
 */
export interface ProfileMarkdownData {
	profile: {
		/** Core, stable user facts (name, preferences, goals) */
		static?: string[]
		/** Recently learned or updated information (current projects, interests) */
		dynamic?: string[]
	}
	searchResults: {
		/** Query-relevant memories based on semantic similarity */
		results: Array<{ memory: string }>
	}
}

export type OutputContentItem =
	| { type: "text"; text: string }
	| { type: "reasoning"; text: string }
	| {
			type: "tool-call"
			id: string
			function: { name: string; arguments: string }
	  }
	| { type: "file"; name: string; mediaType: string; data: string }
	| {
			type: "source"
			sourceType: string
			id: string
			url: string
			title: string
	  }

/**
 * Convert profile data to markdown format
 * @param data Profile data with string arrays for static and dynamic memories
 * @returns Markdown string with profile sections
 */
export function convertProfileToMarkdown(data: ProfileMarkdownData): string {
	const sections: string[] = []

	if (data.profile.static && data.profile.static.length > 0) {
		sections.push("## Static Profile")
		sections.push(data.profile.static.map((item) => `- ${item}`).join("\n"))
	}

	if (data.profile.dynamic && data.profile.dynamic.length > 0) {
		sections.push("## Dynamic Profile")
		sections.push(data.profile.dynamic.map((item) => `- ${item}`).join("\n"))
	}

	return sections.join("\n\n")
}

export const getLastUserMessage = (
	params: LanguageModelCallOptions,
): string | undefined => {
	const lastUserMessage = params.prompt
		.slice()
		.reverse()
		.find((prompt: LanguageModelMessage) => prompt.role === "user")

	if (!lastUserMessage) {
		return undefined
	}

	const content = lastUserMessage.content

	// Handle string content directly
	if (typeof content === "string") {
		return content
	}

	// Handle array content - extract text parts
	return content
		.filter((part) => part.type === "text")
		.map((part) => (part as { type: "text"; text: string }).text)
		.join(" ")
}

export const filterOutSupermemories = (content: string) => {
	return content.split("User Supermemories: ")[0]
}