@yottagraph-app/aether-instructions 1.1.21 → 1.1.23
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.
- package/commands/build_my_app.md +51 -4
- package/package.json +1 -1
- package/rules/api.mdc +165 -49
- package/rules/cookbook.mdc +163 -1
package/commands/build_my_app.md
CHANGED
|
@@ -92,7 +92,47 @@ Key capabilities:
|
|
|
92
92
|
|
|
93
93
|
---
|
|
94
94
|
|
|
95
|
-
## Step 4:
|
|
95
|
+
## Step 4: Verify Data Availability
|
|
96
|
+
|
|
97
|
+
Before designing UX, verify that the data your app needs actually exists
|
|
98
|
+
in the knowledge graph. This prevents building features around empty data.
|
|
99
|
+
|
|
100
|
+
**If MCP tools are available:**
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
elemental_get_schema() → list entity types, confirm your target types exist
|
|
104
|
+
elemental_get_entity(entity="Microsoft") → verify entity lookup works with a known entity
|
|
105
|
+
elemental_get_entity(entity="Apple Inc") → try another known entity
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
If schema calls succeed but entity lookups return "not found," that means
|
|
109
|
+
the entity type exists in the schema but has no data. That's a **data
|
|
110
|
+
issue**, not a broken server. Try different, well-known entity names.
|
|
111
|
+
|
|
112
|
+
**If MCP tools are NOT available, use curl:**
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Read credentials from broadchurch.yaml
|
|
116
|
+
GW=$(grep 'url:' broadchurch.yaml | head -1 | sed 's/.*"\(.*\)".*/\1/')
|
|
117
|
+
ORG=$(grep 'org_id:' broadchurch.yaml | sed 's/.*"\(.*\)".*/\1/')
|
|
118
|
+
KEY=$(grep 'qs_api_key:' broadchurch.yaml | sed 's/.*"\(.*\)".*/\1/')
|
|
119
|
+
|
|
120
|
+
# List entity types
|
|
121
|
+
curl -s "$GW/api/qs/$ORG/elemental/metadata/schema" -H "X-Api-Key: $KEY"
|
|
122
|
+
|
|
123
|
+
# Search for a known entity
|
|
124
|
+
curl -s "$GW/api/qs/$ORG/entities/search" \
|
|
125
|
+
-X POST -H "Content-Type: application/json" -H "X-Api-Key: $KEY" \
|
|
126
|
+
-d '{"queries":[{"queryId":1,"query":"Microsoft"}],"maxResults":3,"includeNames":true}'
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Document what you find.** If certain entity types have sparse data, note
|
|
130
|
+
it in your UX plan. Design features around data that actually exists, and
|
|
131
|
+
mark aspirational features (that need more data) as future work.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Step 5: Design the UX
|
|
96
136
|
|
|
97
137
|
Based on the brief, think about the right UX for this specific problem. Do NOT default to a sidebar-with-tabs layout. Consider:
|
|
98
138
|
|
|
@@ -116,7 +156,7 @@ Present the plan to the user and ask for approval before proceeding.
|
|
|
116
156
|
|
|
117
157
|
---
|
|
118
158
|
|
|
119
|
-
## Step
|
|
159
|
+
## Step 6: Build
|
|
120
160
|
|
|
121
161
|
Implement the plan:
|
|
122
162
|
|
|
@@ -128,6 +168,13 @@ Implement the plan:
|
|
|
128
168
|
6. Use Vuetify components and the project's dark theme
|
|
129
169
|
7. Update `DESIGN.md` with what you built
|
|
130
170
|
|
|
171
|
+
**Use the pre-built platform utilities:**
|
|
172
|
+
|
|
173
|
+
- `useElementalSchema()` — schema discovery with caching, flavor/PID lookup helpers
|
|
174
|
+
- `buildGatewayUrl()`, `getApiKey()`, `padNeid()` from `utils/elementalHelpers`
|
|
175
|
+
- `searchEntities()`, `getEntityName()` from `utils/elementalHelpers`
|
|
176
|
+
- `useElementalClient()` from `@yottagraph-app/elemental-api/client`
|
|
177
|
+
|
|
131
178
|
**Follow the project's coding conventions:**
|
|
132
179
|
|
|
133
180
|
- `<script setup lang="ts">` for all Vue components
|
|
@@ -136,7 +183,7 @@ Implement the plan:
|
|
|
136
183
|
|
|
137
184
|
---
|
|
138
185
|
|
|
139
|
-
## Step
|
|
186
|
+
## Step 7: Verify
|
|
140
187
|
|
|
141
188
|
After building, check dependencies are installed and run a build:
|
|
142
189
|
|
|
@@ -151,7 +198,7 @@ Then suggest the user run `npm run dev` to preview their app locally.
|
|
|
151
198
|
|
|
152
199
|
---
|
|
153
200
|
|
|
154
|
-
## Step
|
|
201
|
+
## Step 8: Next Steps
|
|
155
202
|
|
|
156
203
|
> Your app is taking shape! Here's what you can do next:
|
|
157
204
|
>
|
package/package.json
CHANGED
package/rules/api.mdc
CHANGED
|
@@ -28,13 +28,55 @@ For Lovelace **entity types, properties, relationships, and per-source schemas**
|
|
|
28
28
|
|
|
29
29
|
## Test Before You Build
|
|
30
30
|
|
|
31
|
-
**ALWAYS test
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
to server-side code, where you can't inspect responses in the browser console.
|
|
31
|
+
**ALWAYS test data access before writing application code.** The Elemental
|
|
32
|
+
API has response shapes that differ from what the TypeScript types suggest,
|
|
33
|
+
and assumptions about nesting, property formats, and field names will be
|
|
34
|
+
wrong without testing.
|
|
36
35
|
|
|
37
|
-
###
|
|
36
|
+
### Step 1: MCP tools (interactive exploration)
|
|
37
|
+
|
|
38
|
+
**If MCP tools appear in your tool list, start here.** MCP handles entity
|
|
39
|
+
resolution, PID lookups, and NEID formatting automatically — use it to
|
|
40
|
+
verify what data exists and how it's structured.
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
elemental_get_schema() → list all entity types
|
|
44
|
+
elemental_get_schema(flavor="article") → properties for a type
|
|
45
|
+
elemental_get_entity(entity="Apple") → resolve + fetch entity
|
|
46
|
+
elemental_get_related(entity="Apple",
|
|
47
|
+
related_flavor="person") → follow relationships
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
MCP tells you the correct flavor IDs, property IDs, and data shapes. Use
|
|
51
|
+
these to inform your REST implementation.
|
|
52
|
+
|
|
53
|
+
**Verify MCP is working with known-good queries:**
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
elemental_get_schema() → should return flavors + properties
|
|
57
|
+
elemental_get_entity(entity="Microsoft") → should resolve to a company
|
|
58
|
+
elemental_get_entity(entity="Apple Inc") → another known entity
|
|
59
|
+
elemental_health() → server health check
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Interpreting MCP errors — do NOT assume the server is broken:**
|
|
63
|
+
|
|
64
|
+
- `entity not found` or 404 in entity lookup → the entity doesn't exist
|
|
65
|
+
in the knowledge graph, not a connectivity problem. Try a different entity.
|
|
66
|
+
- `failed to get property values: 404` → the entity was resolved but has
|
|
67
|
+
no data for those properties. The MCP server is working correctly.
|
|
68
|
+
- Schema calls succeed but entity calls fail → data is sparse for that
|
|
69
|
+
entity type. Try well-known entities (Microsoft, Apple Inc, JPMorgan).
|
|
70
|
+
- If `elemental_health()` fails → actual connectivity problem.
|
|
71
|
+
|
|
72
|
+
**Key insight:** A 404 from an MCP entity/property call means "not found,"
|
|
73
|
+
not "server broken." Always test with known entities before concluding
|
|
74
|
+
the server is down.
|
|
75
|
+
|
|
76
|
+
### Step 2: curl (verify exact request/response shapes)
|
|
77
|
+
|
|
78
|
+
MCP doesn't cover every REST endpoint (e.g. `/elemental/find` expressions).
|
|
79
|
+
Test those with curl before implementing them in code.
|
|
38
80
|
|
|
39
81
|
The gateway proxy authenticates on your behalf — no Auth0 tokens needed.
|
|
40
82
|
Read `broadchurch.yaml` for the three values you need:
|
|
@@ -48,8 +90,6 @@ Read `broadchurch.yaml` for the three values you need:
|
|
|
48
90
|
Build the request URL as `{gateway.url}/api/qs/{tenant.org_id}/{endpoint}`
|
|
49
91
|
and include the header `X-Api-Key: {gateway.qs_api_key}`.
|
|
50
92
|
|
|
51
|
-
### Example calls
|
|
52
|
-
|
|
53
93
|
```bash
|
|
54
94
|
# Variables — read these from broadchurch.yaml
|
|
55
95
|
GW="https://broadchurch-portal-194773164895.us-central1.run.app"
|
|
@@ -62,6 +102,13 @@ curl -s "$GW/api/qs/$ORG/entities/search" \
|
|
|
62
102
|
-H "X-Api-Key: $KEY" \
|
|
63
103
|
-d '{"queries":[{"queryId":1,"query":"Microsoft"}],"maxResults":3}'
|
|
64
104
|
|
|
105
|
+
# Test a find expression
|
|
106
|
+
curl -s -X POST "$GW/api/qs/$ORG/elemental/find" \
|
|
107
|
+
-H "X-Api-Key: $KEY" \
|
|
108
|
+
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
109
|
+
--data-urlencode 'expression={"type":"is_type","is_type":{"fid":12}}' \
|
|
110
|
+
--data-urlencode 'limit=5'
|
|
111
|
+
|
|
65
112
|
# Get entity properties (form-encoded)
|
|
66
113
|
curl -s -X POST "$GW/api/qs/$ORG/elemental/entities/properties" \
|
|
67
114
|
-H "X-Api-Key: $KEY" \
|
|
@@ -74,6 +121,47 @@ curl -s -X POST "$GW/api/qs/$ORG/elemental/entities/properties" \
|
|
|
74
121
|
`application/x-www-form-urlencoded` with JSON-stringified parameter values.
|
|
75
122
|
All other endpoints accept `application/json`.
|
|
76
123
|
|
|
124
|
+
**Interpreting errors:** 400 = expression syntax is wrong. 500 = expression
|
|
125
|
+
is valid but the query failed (wrong PID, unsupported operator for that
|
|
126
|
+
property type). 200 + empty `eids` = query worked but no results match.
|
|
127
|
+
404 from entity/property endpoints = entity or data doesn't exist (not a
|
|
128
|
+
server error). Always test with known entities (e.g. search for "Microsoft")
|
|
129
|
+
before assuming the API is broken.
|
|
130
|
+
|
|
131
|
+
### Step 3: Implement with confidence
|
|
132
|
+
|
|
133
|
+
Now write your composable or server route, knowing the exact API shapes.
|
|
134
|
+
|
|
135
|
+
## Pre-Built Helpers
|
|
136
|
+
|
|
137
|
+
The template includes composables and utilities that handle common
|
|
138
|
+
Elemental API patterns. **Use these instead of writing from scratch:**
|
|
139
|
+
|
|
140
|
+
### `useElementalSchema()` — Schema Discovery with Caching
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
const { flavors, properties, flavorByName, pidByName, refresh } = useElementalSchema();
|
|
144
|
+
await refresh(); // fetches once, then cached
|
|
145
|
+
const articleFid = flavorByName('article'); // → number | null
|
|
146
|
+
const namePid = pidByName('name'); // → typically 8
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Handles the dual response shapes (`res.schema.flavors` vs `res.flavors`)
|
|
150
|
+
and the `fid`/`findex` naming inconsistency automatically.
|
|
151
|
+
|
|
152
|
+
### `utils/elementalHelpers` — Gateway URL Helpers
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { buildGatewayUrl, getApiKey, padNeid, searchEntities, getEntityName } from '~/utils/elementalHelpers';
|
|
156
|
+
|
|
157
|
+
const url = buildGatewayUrl('entities/search'); // full gateway URL
|
|
158
|
+
const key = getApiKey(); // from runtimeConfig
|
|
159
|
+
const neid = padNeid('4926132345040704022'); // → "04926132345040704022"
|
|
160
|
+
|
|
161
|
+
const results = await searchEntities('Microsoft'); // batch name search
|
|
162
|
+
const name = await getEntityName(neid); // display name lookup
|
|
163
|
+
```
|
|
164
|
+
|
|
77
165
|
## Client Usage
|
|
78
166
|
|
|
79
167
|
All API calls go through `useElementalClient()` from `@yottagraph-app/elemental-api/client`.
|
|
@@ -163,26 +251,46 @@ knowledge of what's in the graph.
|
|
|
163
251
|
|
|
164
252
|
## API Gotchas
|
|
165
253
|
|
|
166
|
-
### `getSchema()` response
|
|
254
|
+
### `getSchema()` response structure differs by endpoint
|
|
255
|
+
|
|
256
|
+
There are two schema endpoints with **different response shapes**:
|
|
167
257
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
258
|
+
| Endpoint | Flavors at | Flavor ID field | Detail level |
|
|
259
|
+
|----------|-----------|-----------------|--------------|
|
|
260
|
+
| `GET /schema` | top-level (`res.flavors`) | `findex` | Rich (display names, units, domains) |
|
|
261
|
+
| `GET /elemental/metadata/schema` | nested (`res.schema.flavors`) | `fid` | Basic (name + type only) |
|
|
262
|
+
|
|
263
|
+
The TypeScript client's `getSchema()` calls `/elemental/metadata/schema`,
|
|
264
|
+
so the response nests data under `.schema`. The generated types may suggest
|
|
265
|
+
top-level access, but it won't work at runtime.
|
|
172
266
|
|
|
173
267
|
```typescript
|
|
174
|
-
// WRONG — will crash
|
|
268
|
+
// WRONG — will crash (data is nested under .schema):
|
|
175
269
|
const res = await client.getSchema();
|
|
176
270
|
const props = res.properties; // undefined!
|
|
177
271
|
|
|
178
|
-
// CORRECT — always
|
|
272
|
+
// CORRECT — always use fallback to handle both shapes:
|
|
179
273
|
const res = await client.getSchema();
|
|
180
274
|
const properties = res.schema?.properties ?? (res as any).properties ?? [];
|
|
181
275
|
const flavors = res.schema?.flavors ?? (res as any).flavors ?? [];
|
|
182
276
|
```
|
|
183
277
|
|
|
184
|
-
|
|
185
|
-
|
|
278
|
+
### Flavor ID field: `fid` vs `findex`
|
|
279
|
+
|
|
280
|
+
The flavor identifier has **different field names** depending on the endpoint:
|
|
281
|
+
`GET /schema` returns `findex`, `/elemental/metadata/schema` returns `fid`.
|
|
282
|
+
Same value, different key. Always use a fallback:
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
const articleFlavor = flavors.find(f => f.name === 'article');
|
|
286
|
+
const articleFid = articleFlavor?.fid ?? articleFlavor?.findex ?? null;
|
|
287
|
+
|
|
288
|
+
// When building a FID lookup map:
|
|
289
|
+
const fidMap = new Map(flavors.map(f => [f.fid ?? f.findex, f.name]));
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
The `is_type` expression in `/elemental/find` always uses the `fid` key
|
|
293
|
+
regardless of which schema endpoint provided the value.
|
|
186
294
|
|
|
187
295
|
### Relationship property values need zero-padding to form valid NEIDs
|
|
188
296
|
|
|
@@ -273,6 +381,23 @@ const docNeids = (res.values ?? []).map((v) => String(v.value).padStart(20, '0')
|
|
|
273
381
|
|
|
274
382
|
See the **cookbook** rule for a full "Get filings for a company" recipe.
|
|
275
383
|
|
|
384
|
+
### Expression language pitfalls
|
|
385
|
+
|
|
386
|
+
These mistakes come up repeatedly when building `/elemental/find` queries:
|
|
387
|
+
|
|
388
|
+
- **Entity type filtering**: Use `is_type` (not `comparison` with pid=0).
|
|
389
|
+
`comparison` requires `pid != 0`.
|
|
390
|
+
- **`string_like` is name-only**: Only works on the name property (PID 8).
|
|
391
|
+
Use `eq` for exact matches on other string properties.
|
|
392
|
+
- **Boolean combinators**: Use `{"type": "and", "and": [...]}` — not
|
|
393
|
+
`conjunction` or any other name.
|
|
394
|
+
- **`lt`/`gt` are numeric-only**: Only work on `data_int` and `data_float`
|
|
395
|
+
properties.
|
|
396
|
+
- **`regex` is not implemented**: Will return an error.
|
|
397
|
+
|
|
398
|
+
Read the "Common Mistakes" section in the **elemental-api skill** (`find.md`)
|
|
399
|
+
for examples of each.
|
|
400
|
+
|
|
276
401
|
### Entity Search
|
|
277
402
|
|
|
278
403
|
Use `client.findEntities()` (`POST /elemental/find`) for entity search.
|
|
@@ -327,17 +452,13 @@ const response = await getArticle(artid);
|
|
|
327
452
|
if (response.status === 404) { /* handle not found */ }
|
|
328
453
|
```
|
|
329
454
|
|
|
330
|
-
## Lovelace MCP Servers
|
|
455
|
+
## Lovelace MCP Servers
|
|
331
456
|
|
|
332
|
-
Four MCP servers
|
|
333
|
-
data exploration.
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
**Before referencing MCP tools, check your available tool list.** If tools
|
|
338
|
-
like `elemental_get_schema` don't appear in your MCP server list, the
|
|
339
|
-
servers aren't connected — just use the REST client and skill docs instead.
|
|
340
|
-
Do not report this as a problem; it's a normal configuration state.
|
|
457
|
+
Four MCP servers may be configured in `.cursor/mcp.json` for interactive
|
|
458
|
+
data exploration. **Check your tool list** — if tools like
|
|
459
|
+
`elemental_get_schema` appear, use them as your primary testing and
|
|
460
|
+
exploration interface before writing code. If they don't appear, the
|
|
461
|
+
servers aren't connected; use curl and the skill docs instead.
|
|
341
462
|
|
|
342
463
|
| Server | What it provides |
|
|
343
464
|
|---|---|
|
|
@@ -346,6 +467,23 @@ Do not report this as a problem; it's a normal configuration state.
|
|
|
346
467
|
| `lovelace-wiki` | Wikipedia entity enrichment |
|
|
347
468
|
| `lovelace-polymarket` | Prediction market data |
|
|
348
469
|
|
|
470
|
+
### MCP Tool Quick Reference
|
|
471
|
+
|
|
472
|
+
| Tool | Purpose | Use to verify... |
|
|
473
|
+
|---|---|---|
|
|
474
|
+
| `elemental_get_schema` | Discover entity types (flavors), properties, and relationships | Flavor IDs, property IDs, data types |
|
|
475
|
+
| `elemental_get_entity` | Look up entity by name or NEID; returns properties | Entity resolution, property shapes |
|
|
476
|
+
| `elemental_get_related` | Related entities with type/relationship filters | Relationship types and traversal |
|
|
477
|
+
| `elemental_get_relationships` | Relationship types and counts between two entities | Edge types between specific entities |
|
|
478
|
+
| `elemental_graph_neighborhood` | Most influential neighbors of an entity | Graph connectivity |
|
|
479
|
+
| `elemental_graph_sentiment` | Sentiment analysis from news articles | Sentiment data availability |
|
|
480
|
+
| `elemental_get_events` | Events for an entity or by search query | Event categories and shapes |
|
|
481
|
+
| `elemental_health` | Health check | Server connectivity |
|
|
482
|
+
|
|
483
|
+
MCP tools handle entity resolution, PID lookups, and NEID formatting
|
|
484
|
+
automatically. Use them to discover IDs and verify data exists before
|
|
485
|
+
building REST-based features with `useElementalClient()`.
|
|
486
|
+
|
|
349
487
|
### Setup
|
|
350
488
|
|
|
351
489
|
`.cursor/mcp.json` is auto-generated by `init-project.js`. If it's missing,
|
|
@@ -353,25 +491,3 @@ run `node init-project.js --local` to regenerate it. For provisioned projects,
|
|
|
353
491
|
the servers route through the Portal Gateway proxy (no credentials needed).
|
|
354
492
|
For local development without a gateway, the servers require an
|
|
355
493
|
`AUTH0_M2M_DEV_TOKEN` environment variable.
|
|
356
|
-
|
|
357
|
-
These servers are also accessible from the browser through the Portal
|
|
358
|
-
Gateway, so you can build MCP tool exploration UIs if needed.
|
|
359
|
-
|
|
360
|
-
### When MCP Servers Are Available
|
|
361
|
-
|
|
362
|
-
If the MCP tools appear in your tool list, you can use them for interactive
|
|
363
|
-
exploration alongside the REST client:
|
|
364
|
-
|
|
365
|
-
| Tool | Purpose |
|
|
366
|
-
|---|---|
|
|
367
|
-
| `elemental_get_schema` | Discover entity types (flavors), properties, and relationships |
|
|
368
|
-
| `elemental_get_entity` | Look up entity by name or NEID; returns properties |
|
|
369
|
-
| `elemental_get_related` | Related entities with type/relationship filters |
|
|
370
|
-
| `elemental_get_relationships` | Relationship types and counts between two entities |
|
|
371
|
-
| `elemental_graph_neighborhood` | Most influential neighbors of an entity |
|
|
372
|
-
| `elemental_graph_sentiment` | Sentiment analysis from news articles |
|
|
373
|
-
| `elemental_get_events` | Events for an entity or by search query |
|
|
374
|
-
| `elemental_health` | Health check |
|
|
375
|
-
|
|
376
|
-
MCP is convenient for schema discovery and entity resolution during planning.
|
|
377
|
-
For building UI features, always use the REST client (`useElementalClient()`).
|
package/rules/cookbook.mdc
CHANGED
|
@@ -388,7 +388,169 @@ Two-column layout with selectable list and detail panel.
|
|
|
388
388
|
</script>
|
|
389
389
|
```
|
|
390
390
|
|
|
391
|
-
## 7.
|
|
391
|
+
## 7. News Feed — Recent Articles with Sentiment
|
|
392
|
+
|
|
393
|
+
Fetch recent articles from the knowledge graph. Uses `useElementalSchema()`
|
|
394
|
+
for runtime flavor/PID discovery and `buildGatewayUrl()` for gateway access.
|
|
395
|
+
|
|
396
|
+
```vue
|
|
397
|
+
<template>
|
|
398
|
+
<div class="d-flex flex-column fill-height pa-4">
|
|
399
|
+
<h1 class="text-h5 mb-4">Recent News</h1>
|
|
400
|
+
<v-alert v-if="error" type="error" variant="tonal" class="mb-4" closable>
|
|
401
|
+
{{ error }}
|
|
402
|
+
</v-alert>
|
|
403
|
+
<v-progress-linear v-if="loading" indeterminate class="mb-4" />
|
|
404
|
+
<v-list v-if="articles.length" lines="three">
|
|
405
|
+
<v-list-item v-for="a in articles" :key="a.neid">
|
|
406
|
+
<template #title>
|
|
407
|
+
<span>{{ a.name || a.neid }}</span>
|
|
408
|
+
<v-chip
|
|
409
|
+
v-if="a.sentiment"
|
|
410
|
+
size="x-small"
|
|
411
|
+
class="ml-2"
|
|
412
|
+
:color="a.sentiment > 0 ? 'success' : a.sentiment < 0 ? 'error' : 'grey'"
|
|
413
|
+
>
|
|
414
|
+
{{ a.sentiment > 0 ? 'Bullish' : a.sentiment < 0 ? 'Bearish' : 'Neutral' }}
|
|
415
|
+
</v-chip>
|
|
416
|
+
</template>
|
|
417
|
+
<template #subtitle>{{ a.neid }}</template>
|
|
418
|
+
</v-list-item>
|
|
419
|
+
</v-list>
|
|
420
|
+
<v-empty-state
|
|
421
|
+
v-else-if="!loading"
|
|
422
|
+
headline="No articles found"
|
|
423
|
+
icon="mdi-newspaper-variant-outline"
|
|
424
|
+
/>
|
|
425
|
+
</div>
|
|
426
|
+
</template>
|
|
427
|
+
|
|
428
|
+
<script setup lang="ts">
|
|
429
|
+
import { useElementalClient } from '@yottagraph-app/elemental-api/client';
|
|
430
|
+
import { padNeid } from '~/utils/elementalHelpers';
|
|
431
|
+
|
|
432
|
+
const client = useElementalClient();
|
|
433
|
+
const { flavorByName, pidByName, refresh: loadSchema } = useElementalSchema();
|
|
434
|
+
|
|
435
|
+
const articles = ref<{ neid: string; name: string; sentiment: number | null }[]>([]);
|
|
436
|
+
const loading = ref(false);
|
|
437
|
+
const error = ref<string | null>(null);
|
|
438
|
+
|
|
439
|
+
onMounted(async () => {
|
|
440
|
+
loading.value = true;
|
|
441
|
+
try {
|
|
442
|
+
await loadSchema();
|
|
443
|
+
const articleFid = flavorByName('article');
|
|
444
|
+
if (!articleFid) {
|
|
445
|
+
error.value = 'Article entity type not found in schema';
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const res = await client.findEntities({
|
|
450
|
+
expression: JSON.stringify({ type: 'is_type', is_type: { fid: articleFid } }),
|
|
451
|
+
limit: 20,
|
|
452
|
+
});
|
|
453
|
+
const neids: string[] = (res as any).eids ?? [];
|
|
454
|
+
|
|
455
|
+
if (!neids.length) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const namePid = pidByName('name');
|
|
460
|
+
const sentimentPid = pidByName('sentiment');
|
|
461
|
+
const pids = [namePid, sentimentPid].filter((p): p is number => p !== null);
|
|
462
|
+
|
|
463
|
+
const props = await client.getPropertyValues({
|
|
464
|
+
eids: JSON.stringify(neids),
|
|
465
|
+
pids: JSON.stringify(pids),
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
const valueMap = new Map<string, Record<number, any>>();
|
|
469
|
+
for (const v of (props as any).values ?? []) {
|
|
470
|
+
const eid = padNeid(v.eid ?? v.entity_id ?? '');
|
|
471
|
+
if (!valueMap.has(eid)) valueMap.set(eid, {});
|
|
472
|
+
valueMap.get(eid)![v.pid] = v.value;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
articles.value = neids.map((neid) => {
|
|
476
|
+
const vals = valueMap.get(neid) ?? {};
|
|
477
|
+
return {
|
|
478
|
+
neid,
|
|
479
|
+
name: namePid ? (vals[namePid] as string) ?? neid : neid,
|
|
480
|
+
sentiment: sentimentPid ? (vals[sentimentPid] as number) ?? null : null,
|
|
481
|
+
};
|
|
482
|
+
});
|
|
483
|
+
} catch (e: any) {
|
|
484
|
+
error.value = e.message || 'Failed to load articles';
|
|
485
|
+
} finally {
|
|
486
|
+
loading.value = false;
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
</script>
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
## 8. Entity Search with Gateway Helpers
|
|
493
|
+
|
|
494
|
+
Simpler version of recipe #1 using the pre-built `searchEntities()` helper.
|
|
495
|
+
|
|
496
|
+
```vue
|
|
497
|
+
<template>
|
|
498
|
+
<div class="d-flex flex-column fill-height pa-4">
|
|
499
|
+
<h1 class="text-h5 mb-4">Entity Search</h1>
|
|
500
|
+
<v-text-field
|
|
501
|
+
v-model="query"
|
|
502
|
+
label="Search entities"
|
|
503
|
+
prepend-inner-icon="mdi-magnify"
|
|
504
|
+
variant="outlined"
|
|
505
|
+
@keyup.enter="search"
|
|
506
|
+
:loading="loading"
|
|
507
|
+
/>
|
|
508
|
+
<v-alert v-if="error" type="error" variant="tonal" class="mt-2" closable>
|
|
509
|
+
{{ error }}
|
|
510
|
+
</v-alert>
|
|
511
|
+
<v-list v-if="results.length" class="mt-4">
|
|
512
|
+
<v-list-item
|
|
513
|
+
v-for="r in results"
|
|
514
|
+
:key="r.neid"
|
|
515
|
+
:title="r.name"
|
|
516
|
+
:subtitle="r.neid"
|
|
517
|
+
/>
|
|
518
|
+
</v-list>
|
|
519
|
+
<v-empty-state
|
|
520
|
+
v-else-if="searched && !loading"
|
|
521
|
+
headline="No results"
|
|
522
|
+
icon="mdi-magnify-remove-outline"
|
|
523
|
+
/>
|
|
524
|
+
</div>
|
|
525
|
+
</template>
|
|
526
|
+
|
|
527
|
+
<script setup lang="ts">
|
|
528
|
+
import { searchEntities } from '~/utils/elementalHelpers';
|
|
529
|
+
|
|
530
|
+
const query = ref('');
|
|
531
|
+
const results = ref<{ neid: string; name: string }[]>([]);
|
|
532
|
+
const loading = ref(false);
|
|
533
|
+
const error = ref<string | null>(null);
|
|
534
|
+
const searched = ref(false);
|
|
535
|
+
|
|
536
|
+
async function search() {
|
|
537
|
+
if (!query.value.trim()) return;
|
|
538
|
+
loading.value = true;
|
|
539
|
+
error.value = null;
|
|
540
|
+
searched.value = true;
|
|
541
|
+
try {
|
|
542
|
+
results.value = await searchEntities(query.value.trim());
|
|
543
|
+
} catch (e: any) {
|
|
544
|
+
error.value = e.message || 'Search failed';
|
|
545
|
+
results.value = [];
|
|
546
|
+
} finally {
|
|
547
|
+
loading.value = false;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
</script>
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
## 9. Get Filings for a Company
|
|
392
554
|
|
|
393
555
|
Fetch Edgar filings (or any relationship-linked documents) for an organization.
|
|
394
556
|
Uses `$fetch` for the initial entity search because `POST /entities/search`
|