Skip to content

Commit 1c16a11

Browse files
authored
Merge branch 'longview-labs:main' into main
2 parents 3d3964a + 9c2a6c9 commit 1c16a11

2 files changed

Lines changed: 143 additions & 23 deletions

File tree

docs/tooltips.json

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,84 @@
3737
"definition": "Every single AO process and message is stored and read from Arweave. It acts as the state layer for each process, while AO nodes read from it and perform computation.",
3838
"link": "https://www.arweave.org/"
3939
},
40+
{
41+
"term": "ArDrive",
42+
"headline": "Permanent cloud storage platform",
43+
"definition": "ArDrive is like your favourite cloud storage app, but pay once, store permanently. Users can upload files, pages, and more.",
44+
"link": "https://ardrive.io/"
45+
},
46+
{
47+
"term": "AOX",
48+
"headline": "AOX is a cross-chain bridge for AO and Arweave",
49+
"definition": "Users can bring wrapped assets in and out of the AO ecosystem, and can bridge assets from both Arweave and Ethereum.",
50+
"link": "https://aox.xyz"
51+
},
52+
{
53+
"term": "Astro",
54+
"headline": "AstroUSD ($USDA) is AO's first native stablecoin",
55+
"definition": "Astro aims to solve liquidity fragementation by acting a a universal conversion layer for different kinds of stablecoins bridged to AO.",
56+
"link": "https://astrousd.com"
57+
},
58+
{
59+
"term": "Beacon Wallet",
60+
"headline": "Beacon is a native iOS mobile wallet for AO and Arweave",
61+
"definition": "Beacon includes features such as 2FA, multi-sigs, built-in app integrations, and social recovery.",
62+
"link": "https://beaconwallet.app"
63+
},
64+
{
65+
"term": "BetterIDEa",
66+
"headline": "A web-based IDE for developing applications on AO",
67+
"definition": "BetterIDEa is a web-based IDE for developing applications on AO. It has built-in support for the AO Package Manager (APM) and other features for developing processes on AO.",
68+
"link": "https://betteridea.dev"
69+
},
70+
{
71+
"term": "Botega",
72+
"headline": "Botega is a decentralized exchange (DEX) and DeFi platform built on AO",
73+
"definition": "It is fully decentralized with the frontend stored on Arweave and backend processes on AO, and is focused on supporting sophisticated strategies and autonomous trading agents.",
74+
"link": "https://botega.arweave.net"
75+
},
76+
{
77+
"term": "Dexi",
78+
"headline": "Dexi is financial data platform for AO",
79+
"definition": "Dexi aggregates data from liquidity pools agents deployed on AO, and siplays data on trading volume, asset prices, fluctuations, and more.",
80+
"link": "https://dexi.arweave.net"
81+
},
82+
{
83+
"term": "LiquidOps",
84+
"headline": "LiquidOps is a lending and borrowing platform built on AO",
85+
"definition": "Users can accrue interest by providing liquidty, and lenders can take overcollateralized loans from the protocol.",
86+
"link": "https://liquidops.io"
87+
},
88+
{
89+
"term": "Load Network",
90+
"headline": "Load is an \"onchain data center\" focused on scaling EVM storage",
91+
"definition": "Load Network is focused on scaling EVM storage via Arweave, and building out other developer solutions and infrastructure for uploading data to the Permaweb.",
92+
"link": "https://www.load.network/"
93+
},
4094
{
4195
"term": "Lua",
4296
"headline": "Lua is the programming language used for AO processes",
4397
"definition": "Lua is a dynamically-typed, lightweight programming language used to write processes on AO.",
4498
"link": "https://learnxinyminutes.com/lua/"
4599
},
100+
{
101+
"term": "Permaswap",
102+
"headline": "Permaswap is a decentralized exchange (DEX) in the AO ecosystem",
103+
"definition": "Users can swap between assets such as AO, wrapped AR, and more, and can place limit orders and set up a custom slippage amount.",
104+
"link": "https://permaswap.network"
105+
},
106+
{
107+
"term": "PerplexFi",
108+
"headline": "A perpetual trading platform built on AO",
109+
"definition": "Perplex is a perpetual futures trading platform built fully onchain, on AO. Users can trade perpetual contracts with leverage, provide liquidty and earn yield from pools, and more.",
110+
"link": "https://perplex.finance"
111+
},
112+
{
113+
"term": "ProtocolLand",
114+
"headline": "Onchain hosting platform for code repositories",
115+
"definition": "ProtocolLand is like GitHub, but built on Arweave and AO. Users can store their code repositories and deploy websites and apps through built-in integrations.",
116+
"link": "https://protocol.land"
117+
},
46118
{
47119
"term": "Wander Wallet",
48120
"headline": "Browser extension and mobile wallet for Arweave and AO",

sync.py

Lines changed: 71 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22
import json
33
import logging
44
from pathlib import Path
5-
from typing import Dict, Any, List, Optional
5+
from typing import Dict, Any, List, Optional, Set
66
import weaviate
77
from weaviate.util import generate_uuid5
88
from weaviate.classes.init import Auth
99
from weaviate.classes.config import Configure, Property, DataType
10+
from weaviate.exceptions import WeaviateBaseError
1011
# from dotenv import load_dotenv
1112

12-
# load_dotenv()
13+
# load_dotenv(override=True)
1314

14-
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
15+
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s\n")
1516

1617
class WeaviateSyncer:
1718
def __init__(self):
@@ -33,59 +34,106 @@ def ensure_collection(self, name: str, properties: List[Property]):
3334
)
3435
logging.info(f"Created collection '{name}'")
3536

37+
def get_existing_uuids(self, collection_name: str, batch_size: int = 100) -> Set[str]:
38+
collection = self.client.collections.get(collection_name)
39+
uuids = set()
40+
offset = 0
41+
42+
while True:
43+
response = collection.query.fetch_objects(limit=batch_size, offset=offset)
44+
objects = response.objects
45+
if not objects:
46+
break
47+
for obj in objects:
48+
uuids.add(str(obj.uuid))
49+
offset += len(objects)
50+
51+
return uuids
52+
3653
def upsert_entry(self, collection_name: str, properties: Dict[str, Any], uuid_key: str):
3754
collection = self.client.collections.get(collection_name)
38-
uuid = generate_uuid5(properties[uuid_key])
55+
stable_id = properties[uuid_key].strip().lower()
56+
uuid = generate_uuid5(stable_id)
57+
3958
try:
4059
collection.data.insert(properties=properties, uuid=uuid)
41-
logging.info(f"Inserted: {properties.get(uuid_key, '')[:50]}")
60+
logging.info(f"Inserted: {properties.get(uuid_key, '')[:50]} (UUID: {uuid})")
4261
except weaviate.exceptions.UnexpectedStatusCodeException as e:
4362
if "already exists" in str(e):
4463
collection.data.update(uuid=uuid, properties=properties)
45-
logging.info(f"Updated: {properties.get(uuid_key, '')[:50]}")
64+
logging.info(f"Updated: {properties.get(uuid_key, '')[:50]} (UUID: {uuid})")
4665
else:
4766
logging.error(f"Error inserting: {e}")
4867

49-
def sync_from_json(self, collection_name: str, schema_props: List[Property], json_path: Path, uuid_key: str):
50-
self.ensure_collection(collection_name, schema_props)
51-
if not json_path.exists():
52-
logging.warning(f"File not found: {json_path}")
53-
return
68+
return uuid
69+
70+
def delete_entry(self, collection_name: str, uuid: str, name_field: str):
71+
collection = self.client.collections.get(collection_name)
5472
try:
55-
with json_path.open(encoding="utf-8") as f:
56-
entries = json.load(f)
57-
except json.JSONDecodeError as e:
58-
logging.error(f"Error loading {json_path.name}: {e}")
59-
return
73+
obj = collection.query.fetch_object_by_id(uuid)
74+
name = obj.properties.get(name_field, "<unknown>") if obj else "<missing>"
75+
logging.info(f"Deleting: {name} (UUID: {uuid})")
76+
collection.data.delete_by_id(uuid)
77+
except WeaviateBaseError as e:
78+
logging.error(f"Failed to delete {uuid}: {e}")
79+
80+
def sync_entries(self, collection_name: str, schema_props: List[Property], entries: List[Dict[str, Any]], uuid_key: str):
81+
self.ensure_collection(collection_name, schema_props)
82+
83+
# Upsert all entries and collect current UUIDs
84+
current_uuids = set()
6085
for entry in entries:
61-
self.upsert_entry(collection_name, entry, uuid_key)
86+
uuid = self.upsert_entry(collection_name, entry, uuid_key)
87+
current_uuids.add(uuid)
88+
89+
# Get what's currently in DB (after upserts)
90+
existing_uuids = self.get_existing_uuids(collection_name)
91+
logging.info(f"Existing uuids: {existing_uuids}")
92+
logging.info(f"Current uuids: {current_uuids}")
93+
to_delete = existing_uuids - current_uuids
94+
logging.info(f"UUIDs to delete: {to_delete}")
95+
96+
# Delete what's no longer present
97+
for uuid in to_delete:
98+
self.delete_entry(collection_name, uuid, uuid_key)
6299

63100
def close(self):
64101
self.client.close()
65102

103+
66104
def main():
67105
syncer = WeaviateSyncer()
68106
try:
69-
# Q&A sync
107+
# --- FAQ Sync ---
70108
qa_props = [
71109
Property(name="question", data_type=DataType.TEXT),
72110
Property(name="answer", data_type=DataType.TEXT),
73111
Property(name="source", data_type=DataType.TEXT),
74112
Property(name="tags", data_type=DataType.TEXT_ARRAY)
75113
]
76-
docs_dir = Path(__file__).resolve().parent / "docs" / "faq"
77-
for file_path in docs_dir.glob("*.json"):
78-
syncer.sync_from_json("QAEntry", qa_props, file_path, "question")
114+
faq_path = Path(__file__).resolve().parent / "docs" / "faq" / "general.json"
115+
if faq_path.exists():
116+
with faq_path.open(encoding="utf-8") as f:
117+
faq_entries = json.load(f)
118+
syncer.sync_entries("QAEntry", qa_props, faq_entries, "question")
119+
else:
120+
logging.warning(f"FAQ file not found: {faq_path}")
79121

80-
# Tooltip sync
122+
# --- Tooltip Sync ---
81123
tooltip_props = [
82124
Property(name="term", data_type=DataType.TEXT),
83125
Property(name="headline", data_type=DataType.TEXT),
84126
Property(name="definition", data_type=DataType.TEXT),
85127
Property(name="link", data_type=DataType.TEXT)
86128
]
87129
tooltip_path = Path(__file__).resolve().parent / "docs" / "tooltips.json"
88-
syncer.sync_from_json("Tooltip", tooltip_props, tooltip_path, "term")
130+
if tooltip_path.exists():
131+
with tooltip_path.open(encoding="utf-8") as f:
132+
tooltip_entries = json.load(f)
133+
syncer.sync_entries("Tooltip", tooltip_props, tooltip_entries, "term")
134+
else:
135+
logging.warning(f"Tooltip file not found: {tooltip_path}")
136+
89137
finally:
90138
syncer.close()
91139

0 commit comments

Comments
 (0)