@positronic/mem0 0.0.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,271 @@
1
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
2
+ try {
3
+ var info = gen[key](arg);
4
+ var value = info.value;
5
+ } catch (error) {
6
+ reject(error);
7
+ return;
8
+ }
9
+ if (info.done) {
10
+ resolve(value);
11
+ } else {
12
+ Promise.resolve(value).then(_next, _throw);
13
+ }
14
+ }
15
+ function _async_to_generator(fn) {
16
+ return function() {
17
+ var self = this, args = arguments;
18
+ return new Promise(function(resolve, reject) {
19
+ var gen = fn.apply(self, args);
20
+ function _next(value) {
21
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
22
+ }
23
+ function _throw(err) {
24
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
25
+ }
26
+ _next(undefined);
27
+ });
28
+ };
29
+ }
30
+ function _ts_generator(thisArg, body) {
31
+ var f, y, t, _ = {
32
+ label: 0,
33
+ sent: function() {
34
+ if (t[0] & 1) throw t[1];
35
+ return t[1];
36
+ },
37
+ trys: [],
38
+ ops: []
39
+ }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
40
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() {
41
+ return this;
42
+ }), g;
43
+ function verb(n) {
44
+ return function(v) {
45
+ return step([
46
+ n,
47
+ v
48
+ ]);
49
+ };
50
+ }
51
+ function step(op) {
52
+ if (f) throw new TypeError("Generator is already executing.");
53
+ while(g && (g = 0, op[0] && (_ = 0)), _)try {
54
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
55
+ if (y = 0, t) op = [
56
+ op[0] & 2,
57
+ t.value
58
+ ];
59
+ switch(op[0]){
60
+ case 0:
61
+ case 1:
62
+ t = op;
63
+ break;
64
+ case 4:
65
+ _.label++;
66
+ return {
67
+ value: op[1],
68
+ done: false
69
+ };
70
+ case 5:
71
+ _.label++;
72
+ y = op[1];
73
+ op = [
74
+ 0
75
+ ];
76
+ continue;
77
+ case 7:
78
+ op = _.ops.pop();
79
+ _.trys.pop();
80
+ continue;
81
+ default:
82
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
83
+ _ = 0;
84
+ continue;
85
+ }
86
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
87
+ _.label = op[1];
88
+ break;
89
+ }
90
+ if (op[0] === 6 && _.label < t[1]) {
91
+ _.label = t[1];
92
+ t = op;
93
+ break;
94
+ }
95
+ if (t && _.label < t[2]) {
96
+ _.label = t[2];
97
+ _.ops.push(op);
98
+ break;
99
+ }
100
+ if (t[2]) _.ops.pop();
101
+ _.trys.pop();
102
+ continue;
103
+ }
104
+ op = body.call(thisArg, _);
105
+ } catch (e) {
106
+ op = [
107
+ 6,
108
+ e
109
+ ];
110
+ y = 0;
111
+ } finally{
112
+ f = t = 0;
113
+ }
114
+ if (op[0] & 5) throw op[1];
115
+ return {
116
+ value: op[0] ? op[1] : void 0,
117
+ done: true
118
+ };
119
+ }
120
+ }
121
+ /**
122
+ * Creates a Mem0 memory provider.
123
+ *
124
+ * @param config - Configuration for the Mem0 provider
125
+ * @returns A MemoryProvider implementation
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * const memory = createMem0Provider({
130
+ * apiKey: process.env.MEM0_API_KEY!,
131
+ * projectId: 'my-project',
132
+ * });
133
+ *
134
+ * const myBrain = brain('my-brain')
135
+ * .withMemory(memory)
136
+ * .brain('agent', async ({ memory }) => {
137
+ * const prefs = await memory.search('user preferences');
138
+ * return { system: `Preferences: ${prefs}`, prompt: 'Help' };
139
+ * });
140
+ * ```
141
+ */ export function createMem0Provider(config) {
142
+ var apiKey = config.apiKey, _config_baseUrl = config.baseUrl, baseUrl = _config_baseUrl === void 0 ? 'https://api.mem0.ai/v1' : _config_baseUrl, orgId = config.orgId, projectId = config.projectId;
143
+ var headers = {
144
+ 'Content-Type': 'application/json',
145
+ Authorization: "Token ".concat(apiKey)
146
+ };
147
+ if (orgId) {
148
+ headers['Mem0-Org-Id'] = orgId;
149
+ }
150
+ if (projectId) {
151
+ headers['Mem0-Project-Id'] = projectId;
152
+ }
153
+ return {
154
+ search: function search(query, scope, options) {
155
+ return _async_to_generator(function() {
156
+ var body, response, errorText, data;
157
+ return _ts_generator(this, function(_state) {
158
+ switch(_state.label){
159
+ case 0:
160
+ body = {
161
+ query: query,
162
+ agent_id: scope.agentId
163
+ };
164
+ if (scope.userId) {
165
+ body.user_id = scope.userId;
166
+ }
167
+ if (options === null || options === void 0 ? void 0 : options.limit) {
168
+ body.limit = options.limit;
169
+ }
170
+ return [
171
+ 4,
172
+ fetch("".concat(baseUrl, "/memories/search/"), {
173
+ method: 'POST',
174
+ headers: headers,
175
+ body: JSON.stringify(body)
176
+ })
177
+ ];
178
+ case 1:
179
+ response = _state.sent();
180
+ if (!!response.ok) return [
181
+ 3,
182
+ 3
183
+ ];
184
+ return [
185
+ 4,
186
+ response.text()
187
+ ];
188
+ case 2:
189
+ errorText = _state.sent();
190
+ throw new Error("Mem0 search failed (".concat(response.status, "): ").concat(errorText));
191
+ case 3:
192
+ return [
193
+ 4,
194
+ response.json()
195
+ ];
196
+ case 4:
197
+ data = _state.sent();
198
+ return [
199
+ 2,
200
+ data.map(function(result) {
201
+ return {
202
+ id: result.id,
203
+ content: result.memory,
204
+ score: result.score,
205
+ metadata: result.metadata
206
+ };
207
+ })
208
+ ];
209
+ }
210
+ });
211
+ })();
212
+ },
213
+ add: function add(messages, scope, options) {
214
+ return _async_to_generator(function() {
215
+ var body, response, errorText;
216
+ return _ts_generator(this, function(_state) {
217
+ switch(_state.label){
218
+ case 0:
219
+ body = {
220
+ messages: messages.map(function(m) {
221
+ return {
222
+ role: m.role,
223
+ content: m.content
224
+ };
225
+ }),
226
+ agent_id: scope.agentId
227
+ };
228
+ if (scope.userId) {
229
+ body.user_id = scope.userId;
230
+ }
231
+ if (options === null || options === void 0 ? void 0 : options.metadata) {
232
+ body.metadata = options.metadata;
233
+ }
234
+ return [
235
+ 4,
236
+ fetch("".concat(baseUrl, "/memories/"), {
237
+ method: 'POST',
238
+ headers: headers,
239
+ body: JSON.stringify(body)
240
+ })
241
+ ];
242
+ case 1:
243
+ response = _state.sent();
244
+ if (!!response.ok) return [
245
+ 3,
246
+ 3
247
+ ];
248
+ return [
249
+ 4,
250
+ response.text()
251
+ ];
252
+ case 2:
253
+ errorText = _state.sent();
254
+ throw new Error("Mem0 add failed (".concat(response.status, "): ").concat(errorText));
255
+ case 3:
256
+ // We don't need to process the response, just ensure it succeeded
257
+ return [
258
+ 4,
259
+ response.json()
260
+ ];
261
+ case 4:
262
+ _state.sent();
263
+ return [
264
+ 2
265
+ ];
266
+ }
267
+ });
268
+ })();
269
+ }
270
+ };
271
+ }
@@ -0,0 +1,284 @@
1
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
2
+ try {
3
+ var info = gen[key](arg);
4
+ var value = info.value;
5
+ } catch (error) {
6
+ reject(error);
7
+ return;
8
+ }
9
+ if (info.done) {
10
+ resolve(value);
11
+ } else {
12
+ Promise.resolve(value).then(_next, _throw);
13
+ }
14
+ }
15
+ function _async_to_generator(fn) {
16
+ return function() {
17
+ var self = this, args = arguments;
18
+ return new Promise(function(resolve, reject) {
19
+ var gen = fn.apply(self, args);
20
+ function _next(value) {
21
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
22
+ }
23
+ function _throw(err) {
24
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
25
+ }
26
+ _next(undefined);
27
+ });
28
+ };
29
+ }
30
+ function _ts_generator(thisArg, body) {
31
+ var f, y, t, _ = {
32
+ label: 0,
33
+ sent: function() {
34
+ if (t[0] & 1) throw t[1];
35
+ return t[1];
36
+ },
37
+ trys: [],
38
+ ops: []
39
+ }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
40
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() {
41
+ return this;
42
+ }), g;
43
+ function verb(n) {
44
+ return function(v) {
45
+ return step([
46
+ n,
47
+ v
48
+ ]);
49
+ };
50
+ }
51
+ function step(op) {
52
+ if (f) throw new TypeError("Generator is already executing.");
53
+ while(g && (g = 0, op[0] && (_ = 0)), _)try {
54
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
55
+ if (y = 0, t) op = [
56
+ op[0] & 2,
57
+ t.value
58
+ ];
59
+ switch(op[0]){
60
+ case 0:
61
+ case 1:
62
+ t = op;
63
+ break;
64
+ case 4:
65
+ _.label++;
66
+ return {
67
+ value: op[1],
68
+ done: false
69
+ };
70
+ case 5:
71
+ _.label++;
72
+ y = op[1];
73
+ op = [
74
+ 0
75
+ ];
76
+ continue;
77
+ case 7:
78
+ op = _.ops.pop();
79
+ _.trys.pop();
80
+ continue;
81
+ default:
82
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
83
+ _ = 0;
84
+ continue;
85
+ }
86
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
87
+ _.label = op[1];
88
+ break;
89
+ }
90
+ if (op[0] === 6 && _.label < t[1]) {
91
+ _.label = t[1];
92
+ t = op;
93
+ break;
94
+ }
95
+ if (t && _.label < t[2]) {
96
+ _.label = t[2];
97
+ _.ops.push(op);
98
+ break;
99
+ }
100
+ if (t[2]) _.ops.pop();
101
+ _.trys.pop();
102
+ continue;
103
+ }
104
+ op = body.call(thisArg, _);
105
+ } catch (e) {
106
+ op = [
107
+ 6,
108
+ e
109
+ ];
110
+ y = 0;
111
+ } finally{
112
+ f = t = 0;
113
+ }
114
+ if (op[0] & 5) throw op[1];
115
+ return {
116
+ value: op[0] ? op[1] : void 0,
117
+ done: true
118
+ };
119
+ }
120
+ }
121
+ import { z } from 'zod';
122
+ /**
123
+ * Schema for the rememberFact tool input
124
+ */ var rememberFactSchema = z.object({
125
+ fact: z.string().describe('The fact or information to remember for later'),
126
+ userId: z.string().optional().describe('Optional user ID to associate this memory with')
127
+ });
128
+ /**
129
+ * Schema for the recallMemories tool input
130
+ */ var recallMemoriesSchema = z.object({
131
+ query: z.string().describe('The search query to find relevant memories'),
132
+ userId: z.string().optional().describe('Optional user ID to scope the search'),
133
+ limit: z.number().optional().default(10).describe('Maximum number of memories to return')
134
+ });
135
+ /**
136
+ * Tool for storing facts in long-term memory.
137
+ *
138
+ * This tool allows the agent to store important information for future reference.
139
+ * Facts are stored with the agent's scope and optionally a user ID.
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * const tools = createMem0Tools();
144
+ *
145
+ * const myBrain = brain('my-brain')
146
+ * .withMemory(memory)
147
+ * .brain('agent', ({ tools: defaultTools }) => ({
148
+ * system: 'You are helpful. Store user preferences with rememberFact.',
149
+ * prompt: 'User says: I prefer dark mode',
150
+ * tools: { ...defaultTools, ...tools },
151
+ * }));
152
+ * ```
153
+ */ export var rememberFact = {
154
+ description: 'Store a fact or piece of information in long-term memory for future reference.\n\nUse this tool when:\n- The user shares a preference (e.g., "I like dark mode", "I prefer brief responses")\n- The user provides important context (e.g., "I\'m working on project X", "I\'m a beginner")\n- You learn something that should persist across conversations\n\nThe fact should be a clear, standalone statement that can be retrieved later.',
155
+ inputSchema: rememberFactSchema,
156
+ execute: function execute(input, context) {
157
+ return _async_to_generator(function() {
158
+ return _ts_generator(this, function(_state) {
159
+ switch(_state.label){
160
+ case 0:
161
+ if (!context.memory) {
162
+ return [
163
+ 2,
164
+ {
165
+ remembered: false,
166
+ fact: input.fact
167
+ }
168
+ ];
169
+ }
170
+ return [
171
+ 4,
172
+ context.memory.add([
173
+ {
174
+ role: 'assistant',
175
+ content: input.fact
176
+ }
177
+ ], {
178
+ userId: input.userId
179
+ })
180
+ ];
181
+ case 1:
182
+ _state.sent();
183
+ return [
184
+ 2,
185
+ {
186
+ remembered: true,
187
+ fact: input.fact
188
+ }
189
+ ];
190
+ }
191
+ });
192
+ })();
193
+ }
194
+ };
195
+ /**
196
+ * Tool for recalling memories from long-term storage.
197
+ *
198
+ * This tool allows the agent to search for and retrieve relevant memories.
199
+ * Memories are searched within the agent's scope and optionally a user ID.
200
+ *
201
+ * @example
202
+ * ```typescript
203
+ * const tools = createMem0Tools();
204
+ *
205
+ * const myBrain = brain('my-brain')
206
+ * .withMemory(memory)
207
+ * .brain('agent', ({ tools: defaultTools }) => ({
208
+ * system: 'Use recallMemories to find relevant user preferences.',
209
+ * prompt: 'What theme does the user prefer?',
210
+ * tools: { ...defaultTools, ...tools },
211
+ * }));
212
+ * ```
213
+ */ export var recallMemories = {
214
+ description: "Search long-term memory for relevant information.\n\nUse this tool to retrieve:\n- User preferences and settings\n- Previous context or decisions\n- Facts learned in earlier interactions\n\nThe query should describe what you're looking for. Results include relevance scores.",
215
+ inputSchema: recallMemoriesSchema,
216
+ execute: function execute(input, context) {
217
+ return _async_to_generator(function() {
218
+ var memories;
219
+ return _ts_generator(this, function(_state) {
220
+ switch(_state.label){
221
+ case 0:
222
+ if (!context.memory) {
223
+ return [
224
+ 2,
225
+ {
226
+ found: 0,
227
+ memories: []
228
+ }
229
+ ];
230
+ }
231
+ return [
232
+ 4,
233
+ context.memory.search(input.query, {
234
+ userId: input.userId,
235
+ limit: input.limit
236
+ })
237
+ ];
238
+ case 1:
239
+ memories = _state.sent();
240
+ return [
241
+ 2,
242
+ {
243
+ found: memories.length,
244
+ memories: memories.map(function(m) {
245
+ return {
246
+ content: m.content,
247
+ relevance: m.score
248
+ };
249
+ })
250
+ }
251
+ ];
252
+ }
253
+ });
254
+ })();
255
+ }
256
+ };
257
+ /**
258
+ * Creates the standard Mem0 memory tools.
259
+ *
260
+ * Returns an object with `rememberFact` and `recallMemories` tools
261
+ * that can be spread into your agent's tools configuration.
262
+ *
263
+ * @returns Object containing memory tools
264
+ *
265
+ * @example
266
+ * ```typescript
267
+ * import { createMem0Tools } from '@positronic/mem0';
268
+ *
269
+ * const memoryTools = createMem0Tools();
270
+ *
271
+ * const myBrain = brain('my-brain')
272
+ * .withMemory(provider)
273
+ * .brain('agent', () => ({
274
+ * system: 'You can remember and recall information.',
275
+ * prompt: 'Help the user',
276
+ * tools: memoryTools,
277
+ * }));
278
+ * ```
279
+ */ export function createMem0Tools() {
280
+ return {
281
+ rememberFact: rememberFact,
282
+ recallMemories: recallMemories
283
+ };
284
+ }
@@ -0,0 +1,41 @@
1
+ import type { Adapter, MemoryProvider } from '@positronic/core';
2
+ /**
3
+ * Configuration for the Mem0 adapter.
4
+ */
5
+ export interface Mem0AdapterConfig {
6
+ /** The memory provider to use for storing conversations */
7
+ provider: MemoryProvider;
8
+ /**
9
+ * Whether to include tool calls in the conversation history.
10
+ * Defaults to false.
11
+ */
12
+ includeToolCalls?: boolean;
13
+ }
14
+ /**
15
+ * Creates a Mem0 adapter that automatically indexes conversations to memory.
16
+ *
17
+ * The adapter listens to brain events and buffers messages from agent steps.
18
+ * When the brain completes or encounters an error, it flushes the buffered
19
+ * messages to the memory provider.
20
+ *
21
+ * @param config - Configuration for the adapter
22
+ * @returns An Adapter implementation
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import { createMem0Adapter, createMem0Provider } from '@positronic/mem0';
27
+ * import { BrainRunner } from '@positronic/core';
28
+ *
29
+ * const provider = createMem0Provider({ apiKey: process.env.MEM0_API_KEY! });
30
+ * const adapter = createMem0Adapter({ provider });
31
+ *
32
+ * const runner = new BrainRunner({
33
+ * adapters: [adapter],
34
+ * client,
35
+ * });
36
+ *
37
+ * await runner.run(myBrain);
38
+ * ```
39
+ */
40
+ export declare function createMem0Adapter(config: Mem0AdapterConfig): Adapter;
41
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAEP,cAAc,EAEf,MAAM,kBAAkB,CAAC;AAG1B;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,QAAQ,EAAE,cAAc,CAAC;IACzB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CA8GpE"}
@@ -0,0 +1,104 @@
1
+ import type { Memory, ScopedMemory, MemorySearchOptions } from '@positronic/core';
2
+ /**
3
+ * Options for formatting memories.
4
+ */
5
+ export interface FormatMemoriesOptions {
6
+ /** Header text to include before the memories list */
7
+ header?: string;
8
+ /** Whether to include relevance scores (if available) */
9
+ includeScores?: boolean;
10
+ /** Text to return if no memories are found */
11
+ emptyText?: string;
12
+ }
13
+ /**
14
+ * Formats memories into a readable string format.
15
+ *
16
+ * @param memories - Array of memories to format
17
+ * @param options - Formatting options
18
+ * @returns Formatted string representation of the memories
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const memories = await memory.search('user preferences');
23
+ * const formatted = formatMemories(memories);
24
+ * // Returns:
25
+ * // 1. User prefers dark mode
26
+ * // 2. User likes concise responses
27
+ *
28
+ * const formattedWithHeader = formatMemories(memories, {
29
+ * header: 'Known user preferences:',
30
+ * includeScores: true,
31
+ * });
32
+ * // Returns:
33
+ * // Known user preferences:
34
+ * // 1. User prefers dark mode (0.95)
35
+ * // 2. User likes concise responses (0.82)
36
+ * ```
37
+ */
38
+ export declare function formatMemories(memories: Memory[], options?: FormatMemoriesOptions): string;
39
+ /**
40
+ * Options for creating a memory-augmented system prompt.
41
+ */
42
+ export interface CreateMemorySystemPromptOptions extends MemorySearchOptions {
43
+ /** Header text before the memories section */
44
+ memoriesHeader?: string;
45
+ /** Whether to include relevance scores */
46
+ includeScores?: boolean;
47
+ }
48
+ /**
49
+ * Creates a system prompt augmented with relevant memories.
50
+ *
51
+ * This is a convenience function that:
52
+ * 1. Fetches relevant memories using the provided query
53
+ * 2. Formats them nicely
54
+ * 3. Appends them to your base system prompt
55
+ *
56
+ * @param memory - The scoped memory instance
57
+ * @param basePrompt - The base system prompt
58
+ * @param query - The search query for finding relevant memories
59
+ * @param options - Search and formatting options
60
+ * @returns The augmented system prompt
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * const myBrain = brain('my-brain')
65
+ * .withMemory(provider)
66
+ * .brain('agent', async ({ memory }) => {
67
+ * const system = await createMemorySystemPrompt(
68
+ * memory,
69
+ * 'You are a helpful assistant.',
70
+ * 'user preferences',
71
+ * { userId: 'user-123', memoriesHeader: '\n\nUser context:' }
72
+ * );
73
+ *
74
+ * return { system, prompt: 'Help me with my task' };
75
+ * });
76
+ * ```
77
+ */
78
+ export declare function createMemorySystemPrompt(memory: ScopedMemory, basePrompt: string, query: string, options?: CreateMemorySystemPromptOptions): Promise<string>;
79
+ /**
80
+ * Creates a memory context block for including in prompts.
81
+ *
82
+ * Similar to createMemorySystemPrompt but returns just the memory
83
+ * context block, not the full system prompt. Useful when you want
84
+ * more control over prompt construction.
85
+ *
86
+ * @param memory - The scoped memory instance
87
+ * @param query - The search query for finding relevant memories
88
+ * @param options - Search and formatting options
89
+ * @returns Formatted memory context block or empty string
90
+ *
91
+ * @example
92
+ * ```typescript
93
+ * const context = await getMemoryContext(memory, 'user preferences', {
94
+ * userId: 'user-123',
95
+ * limit: 5,
96
+ * });
97
+ *
98
+ * const system = `You are helpful.
99
+ *
100
+ * ${context ? `User context:\n${context}` : ''}`;
101
+ * ```
102
+ */
103
+ export declare function getMemoryContext(memory: ScopedMemory, query: string, options?: MemorySearchOptions): Promise<string>;
104
+ //# sourceMappingURL=helpers.d.ts.map