@yottagraph-app/aether-instructions 1.1.18 → 1.1.20

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.
@@ -102,7 +102,7 @@ Once deployed, agents can connect to your MCP server. Add the Cloud Run URL to y
102
102
 
103
103
  - One server per data domain or external service
104
104
  - Keep tools focused and well-documented
105
- - Return structured data (dicts/lists), not raw strings
105
+ - Return structured data (dicts/lists), not raw strings — MCP handles serialization via the protocol. (This differs from ADK agent tools, which should return formatted strings because the LLM reads tool output directly — see the `agents` rule.)
106
106
  - Handle errors by returning descriptive error messages in the response
107
107
  - Use environment variables for API keys and configuration (never hardcode secrets)
108
108
  - For secrets, use GCP Secret Manager and access at runtime
package/rules/pref.mdc CHANGED
@@ -46,13 +46,25 @@ The backing store auto-initializes on first use — no need to call
46
46
 
47
47
  ## Local Development
48
48
 
49
- KV credentials are only available in deployed builds (Vercel auto-injects
50
- them at runtime). In local dev, `getRedis()` returns `null` and KV routes
51
- return `undefined` for reads and silently skip writes. `Pref<T>` still
52
- works with its default value but won't persist across page refreshes.
49
+ KV credentials (`KV_REST_API_URL`, `KV_REST_API_TOKEN`) are only available
50
+ in deployed builds (Vercel auto-injects them at runtime). In local dev,
51
+ `getRedis()` returns `null` and KV routes return `undefined` for reads and
52
+ silently skip writes. `Pref<T>` still works with its default value but
53
+ won't persist across page refreshes.
53
54
 
54
55
  This is expected — push to `main` and test persistence on the deployed build.
55
56
 
57
+ For local-only persistence, use `localStorage` as a lightweight alternative:
58
+
59
+ ```typescript
60
+ const saved = localStorage.getItem('watchlist');
61
+ const watchlist = ref<string[]>(saved ? JSON.parse(saved) : []);
62
+ watch(watchlist, (val) => localStorage.setItem('watchlist', JSON.stringify(val)), { deep: true });
63
+ ```
64
+
65
+ Use `Pref<T>` for production persistence and `localStorage` for local-only
66
+ development when KV isn't available.
67
+
56
68
  **Auth dependency:** All `/api/kv/*` routes call `unsealCookie(event)` to
57
69
  identify the user. In dev mode (no `NUXT_PUBLIC_AUTH0_CLIENT_SECRET` set),
58
70
  this is bypassed automatically using `NUXT_PUBLIC_USER_NAME`. If you set an
@@ -107,22 +119,6 @@ function useMyFeaturePrefs() {
107
119
  - **Key format**: `prefs:users:{userId}:apps:{appId}:settings:general`
108
120
  (doc-style paths converted to colon-separated Redis keys)
109
121
 
110
- ## Local Development Without KV
111
-
112
- When KV credentials (`KV_REST_API_URL`, `KV_REST_API_TOKEN`) aren't configured,
113
- `Pref<T>` still works with its default value but won't persist across page
114
- refreshes. For local dev, use `localStorage` directly as a lightweight
115
- alternative:
116
-
117
- ```typescript
118
- const saved = localStorage.getItem('watchlist');
119
- const watchlist = ref<string[]>(saved ? JSON.parse(saved) : []);
120
- watch(watchlist, (val) => localStorage.setItem('watchlist', JSON.stringify(val)), { deep: true });
121
- ```
122
-
123
- Use `Pref<T>` for production persistence and `localStorage` for local-only
124
- development when KV isn't available.
125
-
126
122
  ## Scope Guidance
127
123
 
128
124
  | App-specific | Global |
package/rules/server.mdc CHANGED
@@ -158,6 +158,110 @@ export function getDb(): NeonQueryFunction | null {
158
158
  }
159
159
  ```
160
160
 
161
+ ## Calling the Elemental API from Server Routes
162
+
163
+ Server routes can call the Elemental API through the Portal Gateway proxy,
164
+ just like client-side code does. The gateway URL, tenant org ID, and API key
165
+ are available via `useRuntimeConfig()`.
166
+
167
+ **NEVER use `readFileSync('broadchurch.yaml')` in server routes.** The YAML
168
+ file is read at build time by `nuxt.config.ts` and its values flow into
169
+ `runtimeConfig`. Nitro serverless functions (Vercel) don't bundle arbitrary
170
+ project files — `readFileSync` will crash with ENOENT in production even
171
+ though it works locally.
172
+
173
+ ```typescript
174
+ export default defineEventHandler(async (event) => {
175
+ const { public: config } = useRuntimeConfig();
176
+
177
+ const gatewayUrl = config.gatewayUrl; // Portal Gateway base URL
178
+ const orgId = config.tenantOrgId; // Tenant org ID (path segment)
179
+ const apiKey = config.qsApiKey; // API key for X-Api-Key header
180
+
181
+ if (!gatewayUrl || !orgId) {
182
+ throw createError({ statusCode: 503, statusMessage: 'Gateway not configured' });
183
+ }
184
+
185
+ const res = await $fetch(`${gatewayUrl}/api/qs/${orgId}/entities/search`, {
186
+ method: 'POST',
187
+ headers: {
188
+ 'Content-Type': 'application/json',
189
+ ...(apiKey && { 'X-Api-Key': apiKey }),
190
+ },
191
+ body: { queries: [{ queryId: 1, query: 'Microsoft' }], maxResults: 5 },
192
+ });
193
+
194
+ return res;
195
+ });
196
+ ```
197
+
198
+ Available runtime config keys (all under `runtimeConfig.public`):
199
+
200
+ | Key | Source | Purpose |
201
+ |---|---|---|
202
+ | `gatewayUrl` | `broadchurch.yaml` → `gateway.url` | Portal Gateway base URL |
203
+ | `tenantOrgId` | `broadchurch.yaml` → `tenant.org_id` | Tenant ID for API path |
204
+ | `qsApiKey` | `broadchurch.yaml` → `gateway.qs_api_key` | API key sent as `X-Api-Key` |
205
+ | `queryServerAddress` | `broadchurch.yaml` → `query_server.url` | Direct QS URL (prefer gateway) |
206
+
207
+ Build the request URL as `{gatewayUrl}/api/qs/{tenantOrgId}/{endpoint}`.
208
+ See the `api` rule for endpoint reference and response shapes.
209
+
210
+ ## Neon Postgres: Handle Missing Tables in GET Routes
211
+
212
+ Tables created by POST/setup routes won't exist on a fresh deployment.
213
+ **Every GET route that queries a table must handle the case where the table
214
+ doesn't exist yet.** Without this, fresh deploys will 500 on every page load
215
+ until the setup route runs.
216
+
217
+ ```typescript
218
+ export default defineEventHandler(async () => {
219
+ const sql = getDb();
220
+ if (!sql) throw createError({ statusCode: 503, statusMessage: 'Database not configured' });
221
+
222
+ try {
223
+ const rows = await sql`SELECT * FROM companies ORDER BY updated_at DESC`;
224
+ return rows;
225
+ } catch (err: any) {
226
+ if (err.message?.includes('does not exist')) {
227
+ return [];
228
+ }
229
+ throw err;
230
+ }
231
+ });
232
+ ```
233
+
234
+ Alternatively, ensure tables exist before querying by calling
235
+ `CREATE TABLE IF NOT EXISTS` at the top of each GET route, or by calling a
236
+ shared setup function:
237
+
238
+ ```typescript
239
+ // server/utils/ensure-tables.ts
240
+ import { getDb } from '~/server/utils/neon';
241
+
242
+ let _initialized = false;
243
+
244
+ export async function ensureTables() {
245
+ if (_initialized) return;
246
+ const sql = getDb();
247
+ if (!sql) return;
248
+
249
+ await sql`CREATE TABLE IF NOT EXISTS companies (
250
+ id SERIAL PRIMARY KEY,
251
+ neid TEXT UNIQUE NOT NULL,
252
+ name TEXT NOT NULL,
253
+ data JSONB DEFAULT '{}',
254
+ updated_at TIMESTAMPTZ DEFAULT NOW()
255
+ )`;
256
+
257
+ _initialized = true;
258
+ }
259
+ ```
260
+
261
+ Then call `await ensureTables()` at the start of any route that reads the
262
+ table. The `_initialized` flag makes it a no-op after the first call within
263
+ the same serverless invocation.
264
+
161
265
  ## Key Differences from Client-Side Code
162
266
 
163
267
  - Server routes run on the server (Node.js), not in the browser
@@ -62,12 +62,14 @@ flavors:
62
62
  description: "A news article or press release being processed"
63
63
  display_name: "Article"
64
64
  mergeability: not_mergeable
65
+ strong_id_properties: ["newsdata_id"]
65
66
  passive: true
66
67
 
67
68
  - name: "publication"
68
69
  description: "A news publication or media outlet identified by its home URL"
69
70
  display_name: "Publication"
70
71
  mergeability: not_mergeable
72
+ strong_id_properties: ["homeUrl"]
71
73
  passive: true
72
74
 
73
75
  # =============================================================================
@@ -389,6 +391,14 @@ properties:
389
391
  domain_flavors: ["article"]
390
392
  passive: true
391
393
 
394
+ - name: "newsdata_id"
395
+ type: string
396
+ description: "Unique article identifier from the NewsData API"
397
+ display_name: "NewsData ID"
398
+ mergeability: not_mergeable
399
+ domain_flavors: ["article"]
400
+ passive: true
401
+
392
402
  - name: "title"
393
403
  type: string
394
404
  description: "Title of the entity"
@@ -11,17 +11,15 @@ This skill provides documentation for the Lovelace Elemental API, the primary in
11
11
 
12
12
  Use this skill when you need to:
13
13
  - Look up entities (companies, people, organizations) by name or ID
14
- - Get sentiment analysis for entities over time
15
- - Find news mentions and articles about entities
16
- - Explore relationships and connections between entities
17
- - Retrieve events involving specific entities
14
+ - Search for entities by type, property values, or relationships using the expression language
15
+ - Get entity metadata (types/flavors, properties)
18
16
  - Build knowledge graphs of entity networks
19
17
 
20
18
  ## Quick Start
21
19
 
22
20
  1. **Find an entity**: Use `entities.md` to look up a company or person by name and get their NEID (Named Entity ID)
23
- 2. **Get information**: Use the NEID to query sentiment, mentions, relationships, or events
24
- 3. **Dive deeper**: Retrieve full article details or explore connected entities
21
+ 2. **Get information**: Use the NEID to query entity properties or explore the graph
22
+ 3. **Search**: Use `find.md` for expression-based entity searches
25
23
 
26
24
  ## Files in This Skill
27
25
 
@@ -29,9 +27,5 @@ See [overview.md](overview.md) for descriptions of each endpoint category:
29
27
  - `entities.md` - Entity search, details, and properties
30
28
  - `find.md` - Expression language for searching entities by type, property values, and relationships
31
29
  - `schema.md` - Data model: entity types (flavors), properties, and schema endpoints
32
- - `sentiment.md` - Sentiment analysis
33
- - `articles.md` - Articles mentioning entities and full article content
34
- - `events.md` - Events involving entities
35
- - `relationships.md` - Entity connections
36
30
  - `graph.md` - Visual graph generation
37
31
  - `server.md` - Server status and health
@@ -2,6 +2,10 @@
2
2
 
3
3
  Entities are the core objects in the Knowledge Graph: companies, people, organizations, products, and other named things that appear in the news. Each entity has a unique **Named Entity ID (NEID)**.
4
4
 
5
+ NEIDs are stable and can be persisted long-term, but may occasionally change if the database is rebuilt or the application switches databases. When an NEID becomes invalid (e.g., resolution returns no results), re-resolve the entity using its canonical name from the previous query result, then redo any downstream operations that depended on it. NEIDs should NEVER be hardcoded in source code.
6
+
7
+ **Looking for property-based search?** To search for entities by type, property values, or relationships using the expression language, see [find.md](find.md).
8
+
5
9
  ## When to Use
6
10
 
7
11
  - You have an entity name and need to find its NEID
@@ -16,28 +20,24 @@ Entities are the core objects in the Knowledge Graph: companies, people, organiz
16
20
  - Example: `00416400910670863867`
17
21
  - Always pad with leading zeros to exactly 20 characters when normalizing
18
22
  - **EID**: The term EID is sometimes used interchangeably with NEID.
23
+ - **nindex**: The term nindex is sometimes used interchangeably with NEID.
19
24
  - **Entity Resolution**: The same real-world entity may have multiple names (e.g., "Apple", "Apple Inc.", "AAPL"). The API resolves these to a single NEID.
20
- - **Entity Types (Flavors)**: Each entity has a type identified by a Flavor ID (FID) (ex. type "ship" is FID 1).
25
+ - **Flavors (Entity Types)**: Each entity has a type identified by a Flavor ID (FID).
21
26
 
22
27
  ## Tips
23
28
 
24
29
  - Always start by searching for the entity to get the correct NEID
25
- - After resolving an entity to an NEID, it's best to display the canonical entity name to the user to help them identify if something went wrong with the resolution.
30
+ - After resolving an entity to an NEID, save and use the the canonical entity name going forward.
26
31
  - It's typically safe to resolve an entity to the top matching NEID. However, sometimes a useful pattern is to let the user give input that the resolution was incorrect, show them a longer list of matches, and let them choose a different resolution.
27
32
  - Entity names are case-insensitive
28
33
 
29
34
  ## Key Endpoints
30
35
 
31
- | What you need | Endpoint | Client method | Returns |
32
- |---------------|----------|---------------|---------|
33
- | Find entities by expression | `POST /elemental/find` | `findEntities()` | Entity IDs matching expression |
34
- | Find entity by name (simple lookup) | `GET /entities/lookup` | `getNEID()` | NEIDs matching a single name query |
35
- | Basic info (name, aliases, type) | `GET /entities/{neid}` | | Quick summary for display |
36
- | Full property values | `POST /elemental/entities/properties` | `getPropertyValues()` | All properties with PIDs, values, timestamps |
37
-
38
- **`findEntities()` vs `getNEID()` for entity search**:
39
- - **`findEntities()`** → `POST /elemental/find` — expression-based search. Supports filtering by type, property value, relationship, and complex nested queries. See `find.md` for the expression language. **Use this for filtered or batch searches.**
40
- - **`getNEID()`** → `GET /entities/lookup` — simple single-entity name lookup via query parameters (`entityName`, `maxResults`). Best for resolving one company/person name quickly.
36
+ | What you need | Endpoint | Returns |
37
+ |---------------|----------|---------|
38
+ | Find entity by name (batch, scored) | `POST /entities/search` | Ranked matches with NEIDs and scores |
39
+ | Basic info (name, aliases, type) | `GET /entities/{neid}` | Quick summary for display |
40
+ | Full property values | `POST /elemental/entities/properties` | All properties with PIDs, values, timestamps |
41
41
 
42
42
  **Important**: When a user asks for "entity properties," clarify which they mean:
43
43
  - Basic info/metadata → use `/entities/{neid}`
@@ -160,57 +160,42 @@ The same pattern applies to the `expression` parameter on `POST /elemental/find`
160
160
 
161
161
  For understanding entity types (flavors), properties, and the data model, see **schema.md**. You'll need the schema because many API responses return only FIDs and PIDs — use it to translate these to human-readable names.
162
162
 
163
+ ## Properties Return Multiple Timestamped Values
163
164
 
164
- <!-- BEGIN GENERATED CONTENT -->
165
-
166
- ## Endpoints
167
-
168
- ### Get entity details
169
-
170
- `GET /entities/{neid}`
171
-
172
- Get details about a named entity including name, aliases, and type. This is an alias for /reports/{neid}. Response is cached for 5 minutes.
173
-
174
- #### Guidance
175
-
176
- Response is wrapped in a 'report' container object. Access entity data via response.report.
177
-
178
- #### Parameters
179
-
180
- | Name | Type | Required | Description |
181
- |------|------|----------|-------------|
182
- | neid | string | yes | Named Entity ID |
165
+ `getPropertyValues` returns **all** recorded values for a property, not just the latest. A single entity and property (e.g. Apple's `company_cik`) may return dozens of rows with different `recorded_at` timestamps — one per filing or data ingestion event. For display, take the first (or latest) value and deduplicate by PID:
183
166
 
184
- #### Responses
167
+ ```typescript
168
+ const byPid = new Map<number, string>();
169
+ for (const v of values) {
170
+ if (!byPid.has(v.pid)) byPid.set(v.pid, v.value);
171
+ }
172
+ ```
185
173
 
186
- | Status | Description |
187
- |--------|-------------|
188
- | 200 | Entity details (`GetNamedEntityReportResponse`) |
189
- | 400 | Invalid NEID (`Error`) |
190
- | 404 | Entity not found (`Error`) |
191
- | 500 | Internal server error (`Error`) |
174
+ ## `include_attributes` Parameter
192
175
 
193
- #### Example
176
+ `getPropertyValues` accepts an `include_attributes` parameter (set to `'true'` as a string) that returns additional metadata on each value. This is essential for relationship properties like `appears_in`, where attributes carry entity-level sentiment scores and article URLs.
194
177
 
195
- **Request:**
178
+ **Note:** The generated TypeScript client types don't include `include_attributes` in the parameter type. Pass it as an extra property — the API accepts it:
196
179
 
197
- ```
198
- GET /entities/00416400910670863867
180
+ ```typescript
181
+ const res = await client.getPropertyValues({
182
+ eids: JSON.stringify([neid]),
183
+ pids: JSON.stringify([appearsInPid]),
184
+ include_attributes: 'true', // Not in TS types, but API accepts it
185
+ } as any);
199
186
  ```
200
187
 
201
- **Response:**
188
+ Attribute values are keyed by **numeric PID**, not by name. Resolve attribute names to PIDs via the schema before accessing them.
202
189
 
203
- ```json
204
- {"report": {"neid": "00416400910670863867", "name": "Apple", "aliases": ["AAPL", "APPLE", "APPLE INC", "Apple Inc"], "type": "organization"}}
205
- ```
190
+ <!-- BEGIN GENERATED CONTENT -->
206
191
 
207
- ---
192
+ ## Endpoints
208
193
 
209
- ### Get entity report
194
+ ### Get entity details
210
195
 
211
- `GET /reports/{neid}`
196
+ `GET /entities/{neid}`
212
197
 
213
- Get a report about a named entity including name, aliases, and type. Response is cached for 5 minutes.
198
+ Get details about a named entity including name, aliases, and type. This is an alias for /reports/{neid}. Response is cached for 5 minutes.
214
199
 
215
200
  #### Guidance
216
201
 
@@ -226,7 +211,7 @@ Response is wrapped in a 'report' container object. Access entity data via respo
226
211
 
227
212
  | Status | Description |
228
213
  |--------|-------------|
229
- | 200 | Entity report (`GetNamedEntityReportResponse`) |
214
+ | 200 | Entity details (`GetNamedEntityReportResponse`) |
230
215
  | 400 | Invalid NEID (`Error`) |
231
216
  | 404 | Entity not found (`Error`) |
232
217
  | 500 | Internal server error (`Error`) |
@@ -236,7 +221,7 @@ Response is wrapped in a 'report' container object. Access entity data via respo
236
221
  **Request:**
237
222
 
238
223
  ```
239
- GET /reports/00416400910670863867
224
+ GET /entities/00416400910670863867
240
225
  ```
241
226
 
242
227
  **Response:**
@@ -247,54 +232,6 @@ GET /reports/00416400910670863867
247
232
 
248
233
  ---
249
234
 
250
- ### Find entities by expression
251
-
252
- `POST /elemental/find`
253
-
254
- Search for entities using a powerful expression language. The expression parameter supports complex nested queries with logical operators, geographic constraints, property comparisons, and more.
255
-
256
- #### Guidance
257
-
258
- CRITICAL: This endpoint REQUIRES Content-Type: application/x-www-form-urlencoded. Sending a JSON body will fail with 400 error. The expression parameter must be URL-encoded form data, not a JSON request body. For the full expression language reference including all expression types, comparison operators, and examples, see find.md.
259
-
260
- #### Request Body
261
-
262
- **Content-Type:** `application/x-www-form-urlencoded`
263
-
264
- | Name | Type | Required | Description |
265
- |------|------|----------|-------------|
266
- | expression | string | yes | JSON-encoded expression object defining the search criteria |
267
- | deadline | any | no | Response deadline in milliseconds or duration format |
268
- | limit | integer | no | Maximum number of entity IDs to return in first response |
269
-
270
- #### Responses
271
-
272
- | Status | Description |
273
- |--------|-------------|
274
- | 200 | Find operation successful (`FindResponse`) |
275
- | 400 | Bad request - invalid parameters or malformed expression (`Error`) |
276
- | 500 | Internal server error (`Error`) |
277
- | 501 | Elemental API capability not enabled (`Error`) |
278
-
279
- #### Example
280
-
281
- **Request:**
282
-
283
- ```
284
- POST /elemental/find
285
- Content-Type: application/x-www-form-urlencoded
286
-
287
- expression={"type":"is_type","is_type":{"fid":10}}&limit=5
288
- ```
289
-
290
- **Response:**
291
-
292
- ```json
293
- {"op_id": "98cc54e9-0108-4361-9c96-18ea97cda7a2", "follow_up": true, "eids": ["01601807036815568643", "08115040994665529432", "02045070050429461063"]}
294
- ```
295
-
296
- ---
297
-
298
235
  ### Get property values for entities
299
236
 
300
237
  `POST /elemental/entities/properties`
@@ -361,18 +298,6 @@ The named entity report
361
298
  | neid | string | Named Entity ID |
362
299
  | type | string | Entity type (flavor) |
363
300
 
364
- ### FindResponse
365
-
366
- | Field | Type | Description |
367
- |-------|------|-------------|
368
- | find | `FindData` | |
369
-
370
- ### FindData
371
-
372
- | Field | Type | Description |
373
- |-------|------|-------------|
374
- | **eids** | string[] | Array of 20-character entity IDs matching the search expression |
375
-
376
301
  ### GetPropertyValuesResponse
377
302
 
378
303
  | Field | Type | Description |
@@ -5,8 +5,8 @@ The `/elemental/find` endpoint searches for entities using a JSON expression lan
5
5
  ## When to Use
6
6
 
7
7
  - You need to search for entities matching specific criteria (type, property values, relationships)
8
- - You need to combine multiple filters (e.g., "ships with name containing PACIFIC")
9
- - You need to find entities connected to a given entity via the knowledge graph
8
+ - You need to combine multiple filters
9
+ - You need to find entities connected to a given entity via the relationship links
10
10
 
11
11
  ## Expression Structure
12
12
 
@@ -124,13 +124,13 @@ Content-Type: application/x-www-form-urlencoded
124
124
  expression={"type":"comparison","comparison":{"operator":"string_like","pid":8,"value":"Apple"}}
125
125
  ```
126
126
 
127
- ### Find ships with "PACIFIC" in the name (combined AND)
127
+ ### Find organizations with "Global" in the name (combined AND)
128
128
 
129
129
  ```
130
130
  POST /elemental/find
131
131
  Content-Type: application/x-www-form-urlencoded
132
132
 
133
- expression={"type":"and","and":[{"type":"is_type","is_type":{"fid":1}},{"type":"comparison","comparison":{"operator":"string_like","pid":8,"value":"PACIFIC"}}]}&limit=50
133
+ expression={"type":"and","and":[{"type":"is_type","is_type":{"fid":10}},{"type":"comparison","comparison":{"operator":"string_like","pid":8,"value":"Global"}}]}&limit=50
134
134
  ```
135
135
 
136
136
  ### Find entities linked to a specific entity
@@ -261,6 +261,12 @@ expression={"type":"is_type","is_type":{"fid":10}}&limit=5
261
261
  |-------|------|-------------|
262
262
  | **is_type** | `IsType` | |
263
263
 
264
+ ### LinkedExpression
265
+
266
+ | Field | Type | Description |
267
+ |-------|------|-------------|
268
+ | **linked** | `Linked` | |
269
+
264
270
  ### Comparison
265
271
 
266
272
  | Field | Type | Description |
@@ -276,4 +282,13 @@ expression={"type":"is_type","is_type":{"fid":10}}&limit=5
276
282
  |-------|------|-------------|
277
283
  | **fid** | integer | Flavor identifier (FID) representing entity type |
278
284
 
285
+ ### Linked
286
+
287
+ | Field | Type | Description |
288
+ |-------|------|-------------|
289
+ | **distance** | integer | Maximum relationship distance to traverse |
290
+ | pids | integer[] | Property identifiers defining the relationship types to follow |
291
+ | to_entity | string | Target entity ID for relationship traversal |
292
+ | direction | string | Direction of relationship traversal. 'outgoing' (default) follows subject->value edges, 'incoming' follows value->subject (reverse) edges, 'both' unions outgoing and incoming results. |
293
+
279
294
  <!-- END GENERATED CONTENT -->
@@ -1,6 +1,6 @@
1
1
  # Graph
2
2
 
3
- The graph endpoints are focused on "graph analysis", providing insights into relationships between different entities. This includes, but is not limited to, visual rendering of a graph.
3
+ The graph endpoints are focused on "graph analysis". This includes, but is not limited to, visual rendering of a graph.
4
4
 
5
5
  ## When to Use
6
6
 
@@ -32,7 +32,7 @@ Get nodes and edges layout data for visualizing a relationship graph. Response i
32
32
 
33
33
  #### Guidance
34
34
 
35
- Only use this endpoint if you care about graph-specific properties. Otherwise it is faster to use /entities/{source_neid}/links/{target_neid}. This should always be called with a non-empty list of neids. A typical pattern is to call /graph/{center_neid}/neighborhood and then use the returned NEIDs to call this endpoint.
35
+ This should always be called with a non-empty list of neids. A typical pattern is to call /graph/{center_neid}/neighborhood and then use the returned NEIDs to call this endpoint.
36
36
 
37
37
  #### Parameters
38
38
 
@@ -77,7 +77,7 @@ Get list of neighboring entities in the relationship graph, optionally filtered
77
77
 
78
78
  #### Guidance
79
79
 
80
- Only use this endpoint if you care about graph-specific properties. Otherwise it is faster to use /entities/{source_neid}/linked. Response includes the center entity itself in the results with a weight of 1.0. The 'neighbors' and 'weights' arrays are parallel (same indices correspond).
80
+ Response includes the center entity itself in the results with a weight of 1.0. The 'neighbors' and 'weights' arrays are parallel (same indices correspond).
81
81
 
82
82
  #### Parameters
83
83
 
@@ -4,26 +4,55 @@ The Elemental API provides access to the Lovelace Knowledge Graph through the Qu
4
4
 
5
5
  ## Core Concepts
6
6
 
7
- ### Named Entity ID (NEID)
7
+ ### Entities
8
+
9
+ Every entity has:
10
+ - **NEID** — a unique Named Entity ID (stable identifier)
11
+ - **Name** — human-readable label (may have aliases)
12
+ - **Flavor** — the entity type (e.g. "organization", "person", "country", "vessel", "event")
13
+
14
+ #### Named Entity ID (NEID)
15
+
8
16
  Every entity in the system has a unique identifier called a NEID. Most API calls require a NEID, so your first step is usually looking up an entity by name to get its NEID.
9
17
 
10
18
  **Format**: 20-character numeric string with zero-padding. Example: `00416400910670863867`
11
19
 
12
20
  When normalizing NEIDs, always pad with leading zeros to exactly 20 characters.
13
21
 
22
+ NEIDs are stable and can be persisted long-term, but may occasionally change if the database is rebuilt or the application switches databases. When an NEID becomes invalid (e.g., resolution returns no results), re-resolve the entity using its canonical name from the previous query result, then redo any downstream operations that depended on it. NEIDs should NEVER be hardcoded in source code.
23
+
14
24
  **Note**: The terms "eid" and "neid" are interchangeable throughout the API. Some endpoints use `neid` and others use `eid`, but they refer to the same entity identifier.
15
25
 
16
- ### Mention Code (MCODE)
17
- A mention is a specific reference to an entity in an article. Each mention has a unique MCODE that links the entity, article, and context together.
26
+ ### Properties
27
+
28
+ Key-value facts attached to entities, scoped by flavor.
29
+
30
+ Examples: nationality, birth_date, industry, total_revenue, employee_count.
31
+
32
+ Use the schema to discover which properties are available for
33
+ a given entity type.
18
34
 
19
- **Format**: `{neid}:{artid}` with colon separator. Example: `00416400910670863867:05433395856363393596`
35
+ ## Relationships
20
36
 
21
- ### Time Ranges
22
- Most endpoints accept `start` and `end` parameters for filtering by date.
37
+ Directed, typed edges between two entities:
38
+ - **Type** e.g. "owns", "board_member_of", "subsidiary_of", "appears_in"
39
+ - **Direction** — relationships go from source (subject) to target (object)
23
40
 
24
- **Required format**: Full ISO 8601 with timezone.
25
- - Correct: `2026-01-28T00:00:00Z`
26
- - Incorrect: `2026-01-28` (missing time and timezone)
41
+ ## Attributes
42
+
43
+ Metadata attached to relationships. For example, the "participant" relationship
44
+ connecting an entity to an event carries:
45
+ - **role** — the entity's role in the event (e.g. "acquirer", "target", "plaintiff")
46
+ - **sentiment** — an impact score for the entity's involvement
47
+
48
+ ## Events
49
+
50
+ Structured occurrences extracted from source data, each with:
51
+ - **Category** — e.g. "Bankruptcy", "IPO", "Layoffs", "Acquisition"
52
+ - **Date** — when the event occurred (YYYY-MM-DD, or YYYY-MM / YYYY for partial dates)
53
+ - **Description** — detailed, objective description of the event
54
+ - **Likelihood** — temporal status: confirmed, ongoing, likely, or speculative
55
+ - **Participants** — entities involved, each with a role and sentiment score
27
56
 
28
57
  ## Endpoint Categories
29
58
 
@@ -31,21 +60,16 @@ Most endpoints accept `start` and `end` parameters for filtering by date.
31
60
  |------|------------------------|
32
61
  | [entities.md](entities.md) | Look up entities by name, get details and properties |
33
62
  | [find.md](find.md) | Search for entities by type, property values, or relationships using the expression language |
34
- | [sentiment.md](sentiment.md) | Get sentiment scores and trends for an entity |
35
- | [articles.md](articles.md) | Get articles that mention an entity, article content, titles, summaries |
36
- | [events.md](events.md) | Find events involving an entity (mergers, lawsuits, etc.) |
37
- | [relationships.md](relationships.md) | Find connections between entities |
63
+ | [schema.md](schema.md) | Understand the data model: entity types (flavors), properties, and their metadata |
38
64
  | [graph.md](graph.md) | Generate visual network graphs |
39
- | [server.md](server.md) | Check server status and capabilities, as well as schema info including flavors and properties. |
40
- | [llm.md](llm.md) | (Not currently available) AI-assisted queries |
65
+ | [server.md](server.md) | Check server status and capabilities |
41
66
 
42
67
  ## Common Workflows
43
68
 
44
- ### "What's the sentiment for Apple?"
45
- 1. `entities.md` → Look up "Apple" to get NEID
46
- 2. `sentiment.md` → Query sentiment with the NEID
47
-
69
+ ### "Find all organizations in a specific sector"
70
+ 1. `schema.md` → Check available properties and flavors
71
+ 2. `find.md` → Use the expression language to search by property values
48
72
 
49
- ### "How are Microsoft and OpenAI connected?"
50
- 1. `entities.md` → Look up both companies to get NEIDs
51
- 2. `relationships.md` → Query links between the two NEIDs
73
+ ### "What companies are related to Apple?"
74
+ 1. `entities.md` → Look up "Apple" to get NEID
75
+ 2. `find.md` → Query linked entities to find related entities