veroq 1.0.0__tar.gz
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.
- veroq-1.0.0/.gitignore +11 -0
- veroq-1.0.0/LICENSE +21 -0
- veroq-1.0.0/PKG-INFO +157 -0
- veroq-1.0.0/README.md +125 -0
- veroq-1.0.0/pyproject.toml +56 -0
- veroq-1.0.0/veroq/__init__.py +87 -0
- veroq-1.0.0/veroq/agent.py +240 -0
- veroq-1.0.0/veroq/async_client.py +316 -0
- veroq-1.0.0/veroq/cli.py +212 -0
- veroq-1.0.0/veroq/client.py +786 -0
- veroq-1.0.0/veroq/exceptions.py +35 -0
- veroq-1.0.0/veroq/types.py +427 -0
veroq-1.0.0/.gitignore
ADDED
veroq-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 VEROQ
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
veroq-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: veroq
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: VEROQ Python SDK — verified intelligence for AI agents. The truth protocol for agentic AI.
|
|
5
|
+
Project-URL: Homepage, https://veroq.ai
|
|
6
|
+
Project-URL: Documentation, https://veroq.ai/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/veroq/veroq-python
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/veroq/veroq-python/issues
|
|
9
|
+
Project-URL: Changelog, https://veroq.ai/docs/changelog
|
|
10
|
+
Author-email: VEROQ <dev@veroq.ai>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: agents,ai,api,financial,intelligence,nlp,sdk,truth,verified,veroq
|
|
14
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.8
|
|
28
|
+
Requires-Dist: requests>=2.20
|
|
29
|
+
Provides-Extra: async
|
|
30
|
+
Requires-Dist: httpx>=0.24; extra == 'async'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# veroq
|
|
34
|
+
|
|
35
|
+
VEROQ Python SDK -- verified intelligence for AI agents. The truth protocol for agentic AI.
|
|
36
|
+
|
|
37
|
+
> **Migrating from `polaris-news`?** This is the official successor. All class names and env vars are backwards compatible. Just change your import from `polaris_news` to `veroq`.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install veroq
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
### Universal Agent Connector (recommended)
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from veroq import Agent
|
|
51
|
+
|
|
52
|
+
agent = Agent() # reads VEROQ_API_KEY (or POLARIS_API_KEY) from env
|
|
53
|
+
result = agent.ask("What's happening with NVDA?")
|
|
54
|
+
print(result.summary)
|
|
55
|
+
|
|
56
|
+
# Full cross-reference -- everything about a ticker in one call
|
|
57
|
+
full = agent.full("AAPL")
|
|
58
|
+
print(full.price, full.technicals, full.earnings)
|
|
59
|
+
|
|
60
|
+
# Subscribe to real-time events
|
|
61
|
+
for event in agent.subscribe(tickers=["NVDA", "AAPL"], events=["brief"]):
|
|
62
|
+
print(event.type, event.ticker, event.data)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Authenticate via CLI
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
veroq login # opens GitHub in your browser -- API key saved automatically
|
|
69
|
+
veroq whoami # check your auth status
|
|
70
|
+
veroq logout # remove saved credentials
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Full Client
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from veroq import VeroqClient
|
|
77
|
+
|
|
78
|
+
client = VeroqClient() # auto-reads saved credentials
|
|
79
|
+
feed = client.feed(category="technology", limit=10)
|
|
80
|
+
for brief in feed.briefs:
|
|
81
|
+
print(brief.headline)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
You can also pass a key explicitly or set the `VEROQ_API_KEY` environment variable. For backwards compatibility, `POLARIS_API_KEY` is also supported.
|
|
85
|
+
|
|
86
|
+
## Backwards Compatibility
|
|
87
|
+
|
|
88
|
+
If you are migrating from `polaris-news`, the following aliases are available:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from veroq import PolarisClient # alias for VeroqClient
|
|
92
|
+
from veroq import PolarisError # alias for VeroqError
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Both `VEROQ_API_KEY` and `POLARIS_API_KEY` environment variables are supported. Credentials from both `~/.veroq/credentials` and `~/.polaris/credentials` are read.
|
|
96
|
+
|
|
97
|
+
## Methods
|
|
98
|
+
|
|
99
|
+
| Method | Description |
|
|
100
|
+
|--------|-------------|
|
|
101
|
+
| `feed(category?, limit?, page?, per_page?, min_confidence?)` | Get the news feed |
|
|
102
|
+
| `brief(brief_id, include_full_text?)` | Get a single brief by ID |
|
|
103
|
+
| `search(query, category?, page?, per_page?, sort?, min_confidence?, from_date?, to_date?, entity?, sentiment?)` | Search briefs |
|
|
104
|
+
| `generate(topic, category?)` | Generate a brief on a topic |
|
|
105
|
+
| `entities(q?, type?, limit?)` | List entities |
|
|
106
|
+
| `entity_briefs(name, role?, limit?, offset?)` | Get briefs for an entity |
|
|
107
|
+
| `trending_entities(limit?)` | Get trending entities |
|
|
108
|
+
| `similar(brief_id, limit?)` | Get similar briefs |
|
|
109
|
+
| `clusters(period?, limit?)` | Get brief clusters |
|
|
110
|
+
| `data(entity?, type?, limit?)` | Get structured data points |
|
|
111
|
+
| `agent_feed(category?, tags?, limit?, min_confidence?)` | Get agent-optimized feed |
|
|
112
|
+
| `compare_sources(brief_id)` | Compare sources for a brief |
|
|
113
|
+
| `trending(period?, limit?)` | Get trending briefs |
|
|
114
|
+
| `verify(claim, context?)` | Fact-check a claim against briefs |
|
|
115
|
+
| `research(query, max_sources?, depth?, category?)` | Deep research on a topic |
|
|
116
|
+
| `stream(categories?)` | Stream briefs via SSE (generator) |
|
|
117
|
+
| `ticker(symbol)` | Get ticker overview |
|
|
118
|
+
| `ticker_prices(symbols)` | Get live prices |
|
|
119
|
+
| `ticker_sentiment(symbol, period?)` | Sentiment breakdown |
|
|
120
|
+
| `ticker_signals(symbol, days?, threshold?)` | Trading signals |
|
|
121
|
+
| `candles(symbol, interval?, range?)` | OHLCV candle data |
|
|
122
|
+
| `technicals(symbol, range?)` | Full technical analysis |
|
|
123
|
+
| `screener(filters)` | Screen stocks by filters |
|
|
124
|
+
| `screener_natural(query)` | Natural language stock screen |
|
|
125
|
+
| `crypto(symbol?)` | Crypto data |
|
|
126
|
+
| `forex(pair?)` | Forex data |
|
|
127
|
+
| `generate_report(ticker, tier?)` | Generate a report |
|
|
128
|
+
|
|
129
|
+
## Error Handling
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from veroq import VeroqClient, AuthenticationError, RateLimitError, NotFoundError
|
|
133
|
+
|
|
134
|
+
client = VeroqClient()
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
brief = client.brief("abc123")
|
|
138
|
+
except AuthenticationError:
|
|
139
|
+
print("Invalid API key")
|
|
140
|
+
except NotFoundError:
|
|
141
|
+
print("Brief not found")
|
|
142
|
+
except RateLimitError as e:
|
|
143
|
+
print(f"Rate limited. Retry after: {e.retry_after}s")
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Streaming
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
client = VeroqClient()
|
|
150
|
+
|
|
151
|
+
for brief in client.stream(categories="technology,science"):
|
|
152
|
+
print(f"[{brief.category}] {brief.headline}")
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Documentation
|
|
156
|
+
|
|
157
|
+
Full API documentation: https://veroq.ai/docs
|
veroq-1.0.0/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# veroq
|
|
2
|
+
|
|
3
|
+
VEROQ Python SDK -- verified intelligence for AI agents. The truth protocol for agentic AI.
|
|
4
|
+
|
|
5
|
+
> **Migrating from `polaris-news`?** This is the official successor. All class names and env vars are backwards compatible. Just change your import from `polaris_news` to `veroq`.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install veroq
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
### Universal Agent Connector (recommended)
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from veroq import Agent
|
|
19
|
+
|
|
20
|
+
agent = Agent() # reads VEROQ_API_KEY (or POLARIS_API_KEY) from env
|
|
21
|
+
result = agent.ask("What's happening with NVDA?")
|
|
22
|
+
print(result.summary)
|
|
23
|
+
|
|
24
|
+
# Full cross-reference -- everything about a ticker in one call
|
|
25
|
+
full = agent.full("AAPL")
|
|
26
|
+
print(full.price, full.technicals, full.earnings)
|
|
27
|
+
|
|
28
|
+
# Subscribe to real-time events
|
|
29
|
+
for event in agent.subscribe(tickers=["NVDA", "AAPL"], events=["brief"]):
|
|
30
|
+
print(event.type, event.ticker, event.data)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Authenticate via CLI
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
veroq login # opens GitHub in your browser -- API key saved automatically
|
|
37
|
+
veroq whoami # check your auth status
|
|
38
|
+
veroq logout # remove saved credentials
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Full Client
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from veroq import VeroqClient
|
|
45
|
+
|
|
46
|
+
client = VeroqClient() # auto-reads saved credentials
|
|
47
|
+
feed = client.feed(category="technology", limit=10)
|
|
48
|
+
for brief in feed.briefs:
|
|
49
|
+
print(brief.headline)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
You can also pass a key explicitly or set the `VEROQ_API_KEY` environment variable. For backwards compatibility, `POLARIS_API_KEY` is also supported.
|
|
53
|
+
|
|
54
|
+
## Backwards Compatibility
|
|
55
|
+
|
|
56
|
+
If you are migrating from `polaris-news`, the following aliases are available:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from veroq import PolarisClient # alias for VeroqClient
|
|
60
|
+
from veroq import PolarisError # alias for VeroqError
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Both `VEROQ_API_KEY` and `POLARIS_API_KEY` environment variables are supported. Credentials from both `~/.veroq/credentials` and `~/.polaris/credentials` are read.
|
|
64
|
+
|
|
65
|
+
## Methods
|
|
66
|
+
|
|
67
|
+
| Method | Description |
|
|
68
|
+
|--------|-------------|
|
|
69
|
+
| `feed(category?, limit?, page?, per_page?, min_confidence?)` | Get the news feed |
|
|
70
|
+
| `brief(brief_id, include_full_text?)` | Get a single brief by ID |
|
|
71
|
+
| `search(query, category?, page?, per_page?, sort?, min_confidence?, from_date?, to_date?, entity?, sentiment?)` | Search briefs |
|
|
72
|
+
| `generate(topic, category?)` | Generate a brief on a topic |
|
|
73
|
+
| `entities(q?, type?, limit?)` | List entities |
|
|
74
|
+
| `entity_briefs(name, role?, limit?, offset?)` | Get briefs for an entity |
|
|
75
|
+
| `trending_entities(limit?)` | Get trending entities |
|
|
76
|
+
| `similar(brief_id, limit?)` | Get similar briefs |
|
|
77
|
+
| `clusters(period?, limit?)` | Get brief clusters |
|
|
78
|
+
| `data(entity?, type?, limit?)` | Get structured data points |
|
|
79
|
+
| `agent_feed(category?, tags?, limit?, min_confidence?)` | Get agent-optimized feed |
|
|
80
|
+
| `compare_sources(brief_id)` | Compare sources for a brief |
|
|
81
|
+
| `trending(period?, limit?)` | Get trending briefs |
|
|
82
|
+
| `verify(claim, context?)` | Fact-check a claim against briefs |
|
|
83
|
+
| `research(query, max_sources?, depth?, category?)` | Deep research on a topic |
|
|
84
|
+
| `stream(categories?)` | Stream briefs via SSE (generator) |
|
|
85
|
+
| `ticker(symbol)` | Get ticker overview |
|
|
86
|
+
| `ticker_prices(symbols)` | Get live prices |
|
|
87
|
+
| `ticker_sentiment(symbol, period?)` | Sentiment breakdown |
|
|
88
|
+
| `ticker_signals(symbol, days?, threshold?)` | Trading signals |
|
|
89
|
+
| `candles(symbol, interval?, range?)` | OHLCV candle data |
|
|
90
|
+
| `technicals(symbol, range?)` | Full technical analysis |
|
|
91
|
+
| `screener(filters)` | Screen stocks by filters |
|
|
92
|
+
| `screener_natural(query)` | Natural language stock screen |
|
|
93
|
+
| `crypto(symbol?)` | Crypto data |
|
|
94
|
+
| `forex(pair?)` | Forex data |
|
|
95
|
+
| `generate_report(ticker, tier?)` | Generate a report |
|
|
96
|
+
|
|
97
|
+
## Error Handling
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from veroq import VeroqClient, AuthenticationError, RateLimitError, NotFoundError
|
|
101
|
+
|
|
102
|
+
client = VeroqClient()
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
brief = client.brief("abc123")
|
|
106
|
+
except AuthenticationError:
|
|
107
|
+
print("Invalid API key")
|
|
108
|
+
except NotFoundError:
|
|
109
|
+
print("Brief not found")
|
|
110
|
+
except RateLimitError as e:
|
|
111
|
+
print(f"Rate limited. Retry after: {e.retry_after}s")
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Streaming
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
client = VeroqClient()
|
|
118
|
+
|
|
119
|
+
for brief in client.stream(categories="technology,science"):
|
|
120
|
+
print(f"[{brief.category}] {brief.headline}")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Documentation
|
|
124
|
+
|
|
125
|
+
Full API documentation: https://veroq.ai/docs
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "veroq"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "VEROQ Python SDK — verified intelligence for AI agents. The truth protocol for agentic AI."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "VEROQ", email = "dev@veroq.ai" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["veroq", "verified", "intelligence", "api", "sdk", "ai", "agents", "nlp", "truth", "financial"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 5 - Production/Stable",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.8",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
28
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
29
|
+
"Typing :: Typed",
|
|
30
|
+
]
|
|
31
|
+
dependencies = [
|
|
32
|
+
"requests>=2.20",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.scripts]
|
|
36
|
+
veroq = "veroq.cli:main"
|
|
37
|
+
|
|
38
|
+
[project.optional-dependencies]
|
|
39
|
+
async = ["httpx>=0.24"]
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Homepage = "https://veroq.ai"
|
|
43
|
+
Documentation = "https://veroq.ai/docs"
|
|
44
|
+
Repository = "https://github.com/veroq/veroq-python"
|
|
45
|
+
"Bug Tracker" = "https://github.com/veroq/veroq-python/issues"
|
|
46
|
+
Changelog = "https://veroq.ai/docs/changelog"
|
|
47
|
+
|
|
48
|
+
[tool.hatch.build.targets.sdist]
|
|
49
|
+
include = ["veroq/", "LICENSE", "README.md"]
|
|
50
|
+
|
|
51
|
+
[tool.hatch.build.targets.wheel]
|
|
52
|
+
packages = ["veroq"]
|
|
53
|
+
|
|
54
|
+
[tool.pytest.ini_options]
|
|
55
|
+
testpaths = ["tests"]
|
|
56
|
+
asyncio_mode = "auto"
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
__version__ = "1.0.0"
|
|
2
|
+
|
|
3
|
+
from .client import VeroqClient
|
|
4
|
+
from .agent import Agent, AskResult, FullResult, SubscribeEvent
|
|
5
|
+
from .exceptions import APIError, AuthenticationError, NotFoundError, VeroqError, RateLimitError
|
|
6
|
+
from .types import (
|
|
7
|
+
Brief,
|
|
8
|
+
Cluster,
|
|
9
|
+
ClustersResponse,
|
|
10
|
+
ComparisonResponse,
|
|
11
|
+
DataPoint,
|
|
12
|
+
DataPointValue,
|
|
13
|
+
DataResponse,
|
|
14
|
+
DepthMetadata,
|
|
15
|
+
EntitiesResponse,
|
|
16
|
+
Entity,
|
|
17
|
+
EntityCrossRef,
|
|
18
|
+
ExtractResponse,
|
|
19
|
+
ExtractResult,
|
|
20
|
+
FeedResponse,
|
|
21
|
+
Provenance,
|
|
22
|
+
ResearchEntity,
|
|
23
|
+
ResearchMetadata,
|
|
24
|
+
ResearchResponse,
|
|
25
|
+
ResearchSourceUsed,
|
|
26
|
+
SearchResponse,
|
|
27
|
+
VerifyBrief,
|
|
28
|
+
VerifyResponse,
|
|
29
|
+
Source,
|
|
30
|
+
SourceAnalysis,
|
|
31
|
+
SourceVerification,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
from .async_client import AsyncVeroqClient
|
|
36
|
+
except ImportError:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
# Backwards compatibility aliases
|
|
40
|
+
PolarisClient = VeroqClient
|
|
41
|
+
PolarisError = VeroqError
|
|
42
|
+
try:
|
|
43
|
+
AsyncPolarisClient = AsyncVeroqClient
|
|
44
|
+
except NameError:
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
__all__ = [
|
|
48
|
+
"VeroqClient",
|
|
49
|
+
"AsyncVeroqClient",
|
|
50
|
+
"VeroqError",
|
|
51
|
+
"AuthenticationError",
|
|
52
|
+
"RateLimitError",
|
|
53
|
+
"NotFoundError",
|
|
54
|
+
"APIError",
|
|
55
|
+
# Backwards compatibility
|
|
56
|
+
"PolarisClient",
|
|
57
|
+
"PolarisError",
|
|
58
|
+
"Agent",
|
|
59
|
+
"AskResult",
|
|
60
|
+
"FullResult",
|
|
61
|
+
"SubscribeEvent",
|
|
62
|
+
"Brief",
|
|
63
|
+
"Source",
|
|
64
|
+
"Entity",
|
|
65
|
+
"Provenance",
|
|
66
|
+
"FeedResponse",
|
|
67
|
+
"SearchResponse",
|
|
68
|
+
"Cluster",
|
|
69
|
+
"ClustersResponse",
|
|
70
|
+
"DataPoint",
|
|
71
|
+
"DataPointValue",
|
|
72
|
+
"DataResponse",
|
|
73
|
+
"EntitiesResponse",
|
|
74
|
+
"ComparisonResponse",
|
|
75
|
+
"SourceAnalysis",
|
|
76
|
+
"ExtractResult",
|
|
77
|
+
"ExtractResponse",
|
|
78
|
+
"ResearchResponse",
|
|
79
|
+
"ResearchEntity",
|
|
80
|
+
"ResearchMetadata",
|
|
81
|
+
"ResearchSourceUsed",
|
|
82
|
+
"DepthMetadata",
|
|
83
|
+
"EntityCrossRef",
|
|
84
|
+
"SourceVerification",
|
|
85
|
+
"VerifyBrief",
|
|
86
|
+
"VerifyResponse",
|
|
87
|
+
]
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"""
|
|
2
|
+
VEROQ Universal Agent Connector
|
|
3
|
+
|
|
4
|
+
The simplest way to give any AI agent verified financial intelligence.
|
|
5
|
+
|
|
6
|
+
from veroq import Agent
|
|
7
|
+
|
|
8
|
+
agent = Agent() # reads VEROQ_API_KEY or POLARIS_API_KEY from env
|
|
9
|
+
result = agent.ask("What's happening with NVDA?")
|
|
10
|
+
print(result.summary)
|
|
11
|
+
|
|
12
|
+
# Full cross-reference — everything about a ticker
|
|
13
|
+
full = agent.full("AAPL")
|
|
14
|
+
print(full.price, full.technicals, full.earnings)
|
|
15
|
+
|
|
16
|
+
# Subscribe to real-time events
|
|
17
|
+
for event in agent.subscribe(tickers=["NVDA", "AAPL"], events=["brief"]):
|
|
18
|
+
print(event)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import json
|
|
22
|
+
import os
|
|
23
|
+
from typing import Any, Dict, Iterator, List, Optional
|
|
24
|
+
|
|
25
|
+
import requests
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AskResult:
|
|
29
|
+
"""Result from /ask — structured financial intelligence."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, data: Dict[str, Any]):
|
|
32
|
+
self._data = data
|
|
33
|
+
self.question: str = data.get("question", "")
|
|
34
|
+
self.intents: List[str] = data.get("intents", [])
|
|
35
|
+
self.tickers: List[str] = data.get("tickers", [])
|
|
36
|
+
self.reasoning: List[str] = data.get("reasoning", [])
|
|
37
|
+
self.summary: str = data.get("summary", "")
|
|
38
|
+
self.data: Dict[str, Any] = data.get("data", {})
|
|
39
|
+
self.credits_used: int = data.get("credits_used", 0)
|
|
40
|
+
|
|
41
|
+
def __repr__(self) -> str:
|
|
42
|
+
return f"AskResult(tickers={self.tickers}, intents={self.intents}, credits={self.credits_used})"
|
|
43
|
+
|
|
44
|
+
def __str__(self) -> str:
|
|
45
|
+
return self.summary
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class FullResult:
|
|
49
|
+
"""Result from /ticker/:symbol/full — complete cross-reference."""
|
|
50
|
+
|
|
51
|
+
def __init__(self, data: Dict[str, Any]):
|
|
52
|
+
self._data = data
|
|
53
|
+
self.ticker: str = data.get("ticker", "")
|
|
54
|
+
self.entity_name: str = data.get("entity_name", "")
|
|
55
|
+
self.sector: str = data.get("sector", "")
|
|
56
|
+
self.price: Dict[str, Any] = data.get("price", {})
|
|
57
|
+
self.sentiment: Dict[str, Any] = data.get("sentiment", {})
|
|
58
|
+
self.technicals: Dict[str, Any] = data.get("technicals", {})
|
|
59
|
+
self.earnings: Dict[str, Any] = data.get("earnings", {})
|
|
60
|
+
self.news: Dict[str, Any] = data.get("news", {})
|
|
61
|
+
self.insider: Dict[str, Any] = data.get("insider", {})
|
|
62
|
+
self.filings: Dict[str, Any] = data.get("filings", {})
|
|
63
|
+
self.analysts: Dict[str, Any] = data.get("analysts", {})
|
|
64
|
+
self.institutions: Dict[str, Any] = data.get("institutions", {})
|
|
65
|
+
|
|
66
|
+
def __repr__(self) -> str:
|
|
67
|
+
return f"FullResult({self.ticker} ${self.price.get('current', '?')})"
|
|
68
|
+
|
|
69
|
+
def __str__(self) -> str:
|
|
70
|
+
lines = [f"{self.entity_name} ({self.ticker})"]
|
|
71
|
+
if self.price.get("current"):
|
|
72
|
+
lines.append(f"Price: ${self.price['current']} ({self.price.get('change_pct', '?')}%)")
|
|
73
|
+
if self.technicals.get("signal"):
|
|
74
|
+
lines.append(f"Signal: {self.technicals['signal']} | RSI: {self.technicals.get('rsi_14', '?')}")
|
|
75
|
+
if self.earnings.get("next_date"):
|
|
76
|
+
lines.append(f"Next Earnings: {self.earnings['next_date']}")
|
|
77
|
+
return "\n".join(lines)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class SubscribeEvent:
|
|
81
|
+
"""A real-time event from /subscribe."""
|
|
82
|
+
|
|
83
|
+
def __init__(self, event_type: str, data: Dict[str, Any]):
|
|
84
|
+
self.type: str = event_type
|
|
85
|
+
self.ticker: Optional[str] = data.get("ticker")
|
|
86
|
+
self.data: Dict[str, Any] = data.get("data", data)
|
|
87
|
+
self.timestamp: str = data.get("timestamp", "")
|
|
88
|
+
|
|
89
|
+
def __repr__(self) -> str:
|
|
90
|
+
return f"SubscribeEvent({self.type}, ticker={self.ticker})"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class Agent:
|
|
94
|
+
"""
|
|
95
|
+
VEROQ Universal Agent Connector.
|
|
96
|
+
|
|
97
|
+
The simplest way to add verified financial intelligence to any AI agent.
|
|
98
|
+
|
|
99
|
+
agent = Agent(api_key="vq_live_...")
|
|
100
|
+
result = agent.ask("What's happening with NVDA?")
|
|
101
|
+
print(result.summary)
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
def __init__(
|
|
105
|
+
self,
|
|
106
|
+
api_key: Optional[str] = None,
|
|
107
|
+
base_url: str = "https://api.thepolarisreport.com",
|
|
108
|
+
timeout: int = 30,
|
|
109
|
+
):
|
|
110
|
+
# Support both VEROQ_API_KEY and POLARIS_API_KEY for backwards compatibility
|
|
111
|
+
self.api_key = api_key or os.environ.get("VEROQ_API_KEY") or os.environ.get("POLARIS_API_KEY", "")
|
|
112
|
+
self.base_url = base_url.rstrip("/")
|
|
113
|
+
self.timeout = timeout
|
|
114
|
+
self._session = requests.Session()
|
|
115
|
+
self._session.headers.update({
|
|
116
|
+
"Authorization": f"Bearer {self.api_key}" if self.api_key else "",
|
|
117
|
+
"Content-Type": "application/json",
|
|
118
|
+
"User-Agent": "veroq-agent/1.0",
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
def ask(self, question: str) -> AskResult:
|
|
122
|
+
"""
|
|
123
|
+
Ask any financial question. Returns structured data + markdown summary.
|
|
124
|
+
|
|
125
|
+
result = agent.ask("What's happening with NVDA?")
|
|
126
|
+
print(result.summary)
|
|
127
|
+
print(result.tickers) # ['NVDA']
|
|
128
|
+
print(result.reasoning) # ['Identified ticker: NVDA', ...]
|
|
129
|
+
print(result.data) # Raw data from all sources
|
|
130
|
+
"""
|
|
131
|
+
resp = self._session.post(
|
|
132
|
+
f"{self.base_url}/api/v1/ask",
|
|
133
|
+
json={"question": question},
|
|
134
|
+
timeout=self.timeout,
|
|
135
|
+
)
|
|
136
|
+
resp.raise_for_status()
|
|
137
|
+
return AskResult(resp.json())
|
|
138
|
+
|
|
139
|
+
def full(self, ticker: str) -> FullResult:
|
|
140
|
+
"""
|
|
141
|
+
Get EVERYTHING about a ticker in one call.
|
|
142
|
+
|
|
143
|
+
result = agent.full("NVDA")
|
|
144
|
+
print(result.price) # {'current': 178.68, 'change_pct': -0.95}
|
|
145
|
+
print(result.technicals) # {'signal': 'neutral', 'rsi_14': 46.4}
|
|
146
|
+
print(result.earnings) # {'next_date': '2026-05-20', ...}
|
|
147
|
+
print(result.insider) # {'transactions': [...], 'total': 20}
|
|
148
|
+
"""
|
|
149
|
+
resp = self._session.get(
|
|
150
|
+
f"{self.base_url}/api/v1/ticker/{ticker.upper()}/full",
|
|
151
|
+
timeout=self.timeout,
|
|
152
|
+
)
|
|
153
|
+
resp.raise_for_status()
|
|
154
|
+
return FullResult(resp.json())
|
|
155
|
+
|
|
156
|
+
def subscribe(
|
|
157
|
+
self,
|
|
158
|
+
tickers: Optional[List[str]] = None,
|
|
159
|
+
events: Optional[List[str]] = None,
|
|
160
|
+
) -> Iterator[SubscribeEvent]:
|
|
161
|
+
"""
|
|
162
|
+
Subscribe to real-time financial events via SSE.
|
|
163
|
+
|
|
164
|
+
for event in agent.subscribe(tickers=["NVDA", "AAPL"], events=["brief"]):
|
|
165
|
+
print(event.type, event.ticker, event.data)
|
|
166
|
+
"""
|
|
167
|
+
params = {}
|
|
168
|
+
if tickers:
|
|
169
|
+
params["tickers"] = ",".join(t.upper() for t in tickers)
|
|
170
|
+
else:
|
|
171
|
+
params["tickers"] = "*"
|
|
172
|
+
if events:
|
|
173
|
+
params["events"] = ",".join(events)
|
|
174
|
+
|
|
175
|
+
resp = self._session.get(
|
|
176
|
+
f"{self.base_url}/api/v1/subscribe",
|
|
177
|
+
params=params,
|
|
178
|
+
stream=True,
|
|
179
|
+
timeout=None, # SSE is long-lived
|
|
180
|
+
headers={**self._session.headers, "Accept": "text/event-stream"},
|
|
181
|
+
)
|
|
182
|
+
resp.raise_for_status()
|
|
183
|
+
|
|
184
|
+
event_type = ""
|
|
185
|
+
data_lines: List[str] = []
|
|
186
|
+
|
|
187
|
+
for line in resp.iter_lines(decode_unicode=True):
|
|
188
|
+
if line is None:
|
|
189
|
+
continue
|
|
190
|
+
line = line.strip()
|
|
191
|
+
if line.startswith("event:"):
|
|
192
|
+
event_type = line[6:].strip()
|
|
193
|
+
elif line.startswith("data:"):
|
|
194
|
+
data_lines.append(line[5:].strip())
|
|
195
|
+
elif line == "" and event_type and data_lines:
|
|
196
|
+
# Complete event
|
|
197
|
+
try:
|
|
198
|
+
data = json.loads("".join(data_lines))
|
|
199
|
+
yield SubscribeEvent(event_type, data)
|
|
200
|
+
except json.JSONDecodeError:
|
|
201
|
+
pass
|
|
202
|
+
event_type = ""
|
|
203
|
+
data_lines = []
|
|
204
|
+
elif line.startswith(":"):
|
|
205
|
+
pass # heartbeat, ignore
|
|
206
|
+
|
|
207
|
+
def run_agent(self, slug: str, **inputs) -> Dict[str, Any]:
|
|
208
|
+
"""
|
|
209
|
+
Run a marketplace agent.
|
|
210
|
+
|
|
211
|
+
result = agent.run_agent("sector-pulse", sector="Technology")
|
|
212
|
+
print(result["summary"])
|
|
213
|
+
"""
|
|
214
|
+
resp = self._session.post(
|
|
215
|
+
f"{self.base_url}/api/v1/agents/run/{slug}",
|
|
216
|
+
json=inputs,
|
|
217
|
+
timeout=120,
|
|
218
|
+
)
|
|
219
|
+
resp.raise_for_status()
|
|
220
|
+
return resp.json()
|
|
221
|
+
|
|
222
|
+
def search(self, query: str, per_page: int = 10) -> Dict[str, Any]:
|
|
223
|
+
"""Search intelligence briefs."""
|
|
224
|
+
resp = self._session.get(
|
|
225
|
+
f"{self.base_url}/api/v1/search",
|
|
226
|
+
params={"q": query, "per_page": per_page},
|
|
227
|
+
timeout=self.timeout,
|
|
228
|
+
)
|
|
229
|
+
resp.raise_for_status()
|
|
230
|
+
return resp.json()
|
|
231
|
+
|
|
232
|
+
def verify(self, claim: str) -> Dict[str, Any]:
|
|
233
|
+
"""Fact-check a claim against the intelligence corpus."""
|
|
234
|
+
resp = self._session.post(
|
|
235
|
+
f"{self.base_url}/api/v1/verify",
|
|
236
|
+
json={"claim": claim},
|
|
237
|
+
timeout=self.timeout,
|
|
238
|
+
)
|
|
239
|
+
resp.raise_for_status()
|
|
240
|
+
return resp.json()
|