aipa-cli 0.1.5__tar.gz → 0.1.7__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.
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/CHANGELOG.md +19 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/PKG-INFO +30 -4
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/README.md +29 -3
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/__init__.py +1 -1
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/app.py +15 -2
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/chat.py +15 -1
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/cli.py +22 -1
- aipa_cli-0.1.7/src/aipriceaction_terminal/cli_setup.py +71 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/settings_tab.py +7 -2
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/user_settings.py +20 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/.gitignore +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/LICENSE +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/pyproject.toml +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/__main__.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/actions.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/agents/__init__.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/agents/agent.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/agents/callbacks.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/agents/config.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/agents/personas.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/agents/tools.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/bindings.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/cli_commands.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/deep_research.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/theme.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/ticker_data.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/utils.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/widgets/__init__.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/widgets/chat_input.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/widgets/ticker_select.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/src/aipriceaction_terminal/workflows.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/tests/conftest.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/tests/openrouter_responses.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/tests/test_app.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/tests/test_chat.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/tests/test_integration.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/tests/test_settings_api.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/tests/test_thinking.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/tests/test_tool_call_streaming.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/tests/test_utils.py +0 -0
- {aipa_cli-0.1.5 → aipa_cli-0.1.7}/tests/test_workflows.py +0 -0
|
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.7] - 2026-05-09
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Bridge settings.json API key to SDK Pydantic settings by seeding env vars (`OPENAI_API_KEY`, `OPENAI_BASE_URL`, `OPENAI_MODEL`) at CLI entry point before any SDK import
|
|
12
|
+
|
|
13
|
+
## [0.1.6] - 2026-05-09
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- Add `aipa setup` interactive CLI command for first-run configuration (language, reference ticker, API key, base URL, model)
|
|
17
|
+
- Add `setup_done` flag to user settings, auto-run setup before commands that need an API key (`analyze`, `deep-research`, TUI)
|
|
18
|
+
- Add "First-Run Setup" section to README documenting which commands require setup
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- Lazy-load `AgentSession` in TUI instead of creating on mount, preventing crash when no API key is configured
|
|
22
|
+
- Guard agent usage in chat (`_run_agent_chat`, `_run_analyze`, `/clear`) and settings tab with `_ensure_agent()`
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- Fix crash on `aipa` launch when no API key is configured (`OpenAIError: Missing credentials`)
|
|
26
|
+
|
|
8
27
|
## [0.1.5] - 2026-05-09
|
|
9
28
|
|
|
10
29
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aipa-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
4
4
|
Summary: Terminal TUI for AI-powered ticker analysis
|
|
5
5
|
Project-URL: Homepage, https://github.com/quanhua92/aipriceaction
|
|
6
6
|
Project-URL: Repository, https://github.com/quanhua92/aipriceaction
|
|
@@ -46,15 +46,18 @@ aipa-cli
|
|
|
46
46
|
## Requirements
|
|
47
47
|
|
|
48
48
|
- Python 3.13+
|
|
49
|
-
- An OpenAI-compatible API key (`OPENAI_API_KEY`)
|
|
49
|
+
- An OpenAI-compatible API key (`OPENAI_API_KEY`) — only needed for AI analysis, not for data fetching
|
|
50
50
|
- Optional: set `OPENAI_BASE_URL` for custom providers like OpenRouter
|
|
51
51
|
|
|
52
52
|
## Quick Start
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
|
-
# Launch the TUI
|
|
55
|
+
# Launch the TUI — first run auto-starts interactive setup
|
|
56
56
|
aipa
|
|
57
57
|
|
|
58
|
+
# Or run setup manually at any time
|
|
59
|
+
aipa setup
|
|
60
|
+
|
|
58
61
|
# AI analysis with default question template
|
|
59
62
|
aipa analyze VCB
|
|
60
63
|
|
|
@@ -166,7 +169,7 @@ aipa deep-research --lang vn
|
|
|
166
169
|
|
|
167
170
|
### `aipa get-ohlcv-data`
|
|
168
171
|
|
|
169
|
-
Fetch raw OHLCV data as a table (no LLM involved).
|
|
172
|
+
Fetch raw OHLCV data as a table (no LLM involved, works without setup).
|
|
170
173
|
|
|
171
174
|
```
|
|
172
175
|
# Default: daily data with EMA indicators
|
|
@@ -191,6 +194,29 @@ aipa get-ohlcv-data BTCUSDT --interval 1D --limit 30
|
|
|
191
194
|
| `--ma` / `--no-ma` | Include/exclude moving averages (default: included) |
|
|
192
195
|
| `--ema` | Use EMA instead of SMA |
|
|
193
196
|
|
|
197
|
+
### `aipa setup`
|
|
198
|
+
|
|
199
|
+
Interactive first-run configuration. Prompts for language, reference ticker, API key, base URL, and model. Settings are saved to `~/.aipriceaction/settings.json`. Re-running shows current values as defaults.
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
# Run interactively
|
|
203
|
+
aipa setup
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## First-Run Setup
|
|
207
|
+
|
|
208
|
+
Commands that require an API key will auto-run `aipa setup` on first use if not yet configured. Commands that don't need an API key always work immediately.
|
|
209
|
+
|
|
210
|
+
| Command | Setup required? |
|
|
211
|
+
|---|---|
|
|
212
|
+
| `aipa get-ohlcv-data` | No setup needed |
|
|
213
|
+
| `aipa analyze VCB --context-only` | No setup needed |
|
|
214
|
+
| `aipa analyze VCB --questions` | No setup needed |
|
|
215
|
+
| `aipa setup` | Runs setup |
|
|
216
|
+
| `aipa` | Auto-runs setup first |
|
|
217
|
+
| `aipa analyze VCB` | Auto-runs setup first |
|
|
218
|
+
| `aipa deep-research` | Auto-runs setup first |
|
|
219
|
+
|
|
194
220
|
## TUI
|
|
195
221
|
|
|
196
222
|
Launch the TUI with `aipa`. The interface has three tabs:
|
|
@@ -21,15 +21,18 @@ aipa-cli
|
|
|
21
21
|
## Requirements
|
|
22
22
|
|
|
23
23
|
- Python 3.13+
|
|
24
|
-
- An OpenAI-compatible API key (`OPENAI_API_KEY`)
|
|
24
|
+
- An OpenAI-compatible API key (`OPENAI_API_KEY`) — only needed for AI analysis, not for data fetching
|
|
25
25
|
- Optional: set `OPENAI_BASE_URL` for custom providers like OpenRouter
|
|
26
26
|
|
|
27
27
|
## Quick Start
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
# Launch the TUI
|
|
30
|
+
# Launch the TUI — first run auto-starts interactive setup
|
|
31
31
|
aipa
|
|
32
32
|
|
|
33
|
+
# Or run setup manually at any time
|
|
34
|
+
aipa setup
|
|
35
|
+
|
|
33
36
|
# AI analysis with default question template
|
|
34
37
|
aipa analyze VCB
|
|
35
38
|
|
|
@@ -141,7 +144,7 @@ aipa deep-research --lang vn
|
|
|
141
144
|
|
|
142
145
|
### `aipa get-ohlcv-data`
|
|
143
146
|
|
|
144
|
-
Fetch raw OHLCV data as a table (no LLM involved).
|
|
147
|
+
Fetch raw OHLCV data as a table (no LLM involved, works without setup).
|
|
145
148
|
|
|
146
149
|
```
|
|
147
150
|
# Default: daily data with EMA indicators
|
|
@@ -166,6 +169,29 @@ aipa get-ohlcv-data BTCUSDT --interval 1D --limit 30
|
|
|
166
169
|
| `--ma` / `--no-ma` | Include/exclude moving averages (default: included) |
|
|
167
170
|
| `--ema` | Use EMA instead of SMA |
|
|
168
171
|
|
|
172
|
+
### `aipa setup`
|
|
173
|
+
|
|
174
|
+
Interactive first-run configuration. Prompts for language, reference ticker, API key, base URL, and model. Settings are saved to `~/.aipriceaction/settings.json`. Re-running shows current values as defaults.
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
# Run interactively
|
|
178
|
+
aipa setup
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## First-Run Setup
|
|
182
|
+
|
|
183
|
+
Commands that require an API key will auto-run `aipa setup` on first use if not yet configured. Commands that don't need an API key always work immediately.
|
|
184
|
+
|
|
185
|
+
| Command | Setup required? |
|
|
186
|
+
|---|---|
|
|
187
|
+
| `aipa get-ohlcv-data` | No setup needed |
|
|
188
|
+
| `aipa analyze VCB --context-only` | No setup needed |
|
|
189
|
+
| `aipa analyze VCB --questions` | No setup needed |
|
|
190
|
+
| `aipa setup` | Runs setup |
|
|
191
|
+
| `aipa` | Auto-runs setup first |
|
|
192
|
+
| `aipa analyze VCB` | Auto-runs setup first |
|
|
193
|
+
| `aipa deep-research` | Auto-runs setup first |
|
|
194
|
+
|
|
169
195
|
## TUI
|
|
170
196
|
|
|
171
197
|
Launch the TUI with `aipa`. The interface has three tabs:
|
|
@@ -38,12 +38,12 @@ class AIPriceActionApp(AppActions, App):
|
|
|
38
38
|
self.ticker = saved["ticker"]
|
|
39
39
|
self.interval = saved["interval"]
|
|
40
40
|
self.language = saved["language"]
|
|
41
|
+
self.agent = None
|
|
42
|
+
self._agent_lang: str | None = None
|
|
41
43
|
from aipriceaction import AIContextBuilder
|
|
42
44
|
from aipriceaction import AIPriceAction as AAPClient
|
|
43
45
|
self.builder = AIContextBuilder(lang=self.language)
|
|
44
46
|
self.client = AAPClient()
|
|
45
|
-
from .agents import AgentSession, AgentConfig
|
|
46
|
-
self.agent = AgentSession(AgentConfig(lang=self.language))
|
|
47
47
|
self._load_ticker_options()
|
|
48
48
|
# Populate SettingsTab widgets with loaded values
|
|
49
49
|
self.query_one("#setting-ticker", Input).value = self.ticker
|
|
@@ -69,6 +69,19 @@ class AIPriceActionApp(AppActions, App):
|
|
|
69
69
|
except Exception as e:
|
|
70
70
|
self.notify(f"Failed to load tickers: {e}", severity="error")
|
|
71
71
|
|
|
72
|
+
def _ensure_agent(self) -> bool:
|
|
73
|
+
"""Lazy-create AgentSession on first use. Returns False if API key is missing."""
|
|
74
|
+
if self.agent is not None and self._agent_lang == self.language:
|
|
75
|
+
return True
|
|
76
|
+
from aipriceaction.settings import settings
|
|
77
|
+
if not settings.openai_api_key:
|
|
78
|
+
self.agent = None
|
|
79
|
+
return False
|
|
80
|
+
from .agents import AgentSession, AgentConfig
|
|
81
|
+
self.agent = AgentSession(AgentConfig(lang=self.language))
|
|
82
|
+
self._agent_lang = self.language
|
|
83
|
+
return True
|
|
84
|
+
|
|
72
85
|
def compose(self) -> ComposeResult:
|
|
73
86
|
yield Header(show_clock=True)
|
|
74
87
|
with TabbedContent(initial="chat"):
|
|
@@ -216,7 +216,8 @@ class ChatTab(Vertical):
|
|
|
216
216
|
)
|
|
217
217
|
elif cmd == "/clear":
|
|
218
218
|
log.clear()
|
|
219
|
-
self.app.agent
|
|
219
|
+
if self.app.agent is not None:
|
|
220
|
+
self.app.agent.clear_history()
|
|
220
221
|
elif cmd == "/exit":
|
|
221
222
|
self.app.exit()
|
|
222
223
|
elif cmd == "/analyze":
|
|
@@ -322,6 +323,13 @@ class ChatTab(Vertical):
|
|
|
322
323
|
"""Build context and stream AI analysis for a ticker."""
|
|
323
324
|
log = self.query_one("#chat-log", RichLog)
|
|
324
325
|
try:
|
|
326
|
+
if not self.app._ensure_agent():
|
|
327
|
+
log.write(
|
|
328
|
+
"[bold yellow]API key not configured.[/bold yellow]\n"
|
|
329
|
+
"Set it in the Settings tab or run [bold]aipa setup[/bold]."
|
|
330
|
+
)
|
|
331
|
+
return
|
|
332
|
+
|
|
325
333
|
builder = self.app.builder
|
|
326
334
|
|
|
327
335
|
# Build context without system prompt (agent has it already)
|
|
@@ -389,6 +397,12 @@ class ChatTab(Vertical):
|
|
|
389
397
|
async def _run_agent_chat(self, message: str) -> None:
|
|
390
398
|
"""Stream an agent response into the chat log."""
|
|
391
399
|
log = self.query_one("#chat-log", RichLog)
|
|
400
|
+
if not self.app._ensure_agent():
|
|
401
|
+
log.write(
|
|
402
|
+
"[bold yellow]API key not configured.[/bold yellow]\n"
|
|
403
|
+
"Set it in the Settings tab or run [bold]aipa setup[/bold]."
|
|
404
|
+
)
|
|
405
|
+
return
|
|
392
406
|
try:
|
|
393
407
|
await stream_agent_to_log(
|
|
394
408
|
log,
|
|
@@ -3,7 +3,18 @@
|
|
|
3
3
|
import argparse
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
def _ensure_setup() -> None:
|
|
7
|
+
"""Run interactive setup if setup_done is not set."""
|
|
8
|
+
from .user_settings import load_settings
|
|
9
|
+
if not load_settings().get("setup_done"):
|
|
10
|
+
from .cli_setup import cmd_setup
|
|
11
|
+
cmd_setup()
|
|
12
|
+
|
|
13
|
+
|
|
6
14
|
def run():
|
|
15
|
+
from .user_settings import apply_settings_to_env
|
|
16
|
+
apply_settings_to_env()
|
|
17
|
+
|
|
7
18
|
parser = argparse.ArgumentParser(prog="aipa", description="AIPriceAction terminal")
|
|
8
19
|
sub = parser.add_subparsers(dest="command")
|
|
9
20
|
|
|
@@ -44,15 +55,24 @@ def run():
|
|
|
44
55
|
p_deep.add_argument("--output", default=None, help="Save final report to file")
|
|
45
56
|
p_deep.add_argument("--lang", default=None, choices=["en", "vn"], help="Override language")
|
|
46
57
|
|
|
58
|
+
# aipa setup
|
|
59
|
+
sub.add_parser("setup", help="Interactive first-run setup")
|
|
60
|
+
|
|
47
61
|
args = parser.parse_args()
|
|
48
62
|
|
|
49
|
-
if args.command == "
|
|
63
|
+
if args.command == "setup":
|
|
64
|
+
from .cli_setup import cmd_setup
|
|
65
|
+
cmd_setup()
|
|
66
|
+
elif args.command == "analyze":
|
|
67
|
+
if not getattr(args, "context_only", False) and not getattr(args, "questions", False):
|
|
68
|
+
_ensure_setup()
|
|
50
69
|
from .cli_commands import cmd_analyze
|
|
51
70
|
cmd_analyze(args)
|
|
52
71
|
elif args.command == "get-ohlcv-data":
|
|
53
72
|
from .cli_commands import cmd_get_ohlcv
|
|
54
73
|
cmd_get_ohlcv(args)
|
|
55
74
|
elif args.command == "deep-research":
|
|
75
|
+
_ensure_setup()
|
|
56
76
|
from .cli_commands import cmd_deep_research
|
|
57
77
|
cmd_deep_research(
|
|
58
78
|
question=" ".join(args.question) if args.question else "",
|
|
@@ -61,5 +81,6 @@ def run():
|
|
|
61
81
|
lang=args.lang,
|
|
62
82
|
)
|
|
63
83
|
else:
|
|
84
|
+
_ensure_setup()
|
|
64
85
|
from .app import main
|
|
65
86
|
main()
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Interactive first-run setup for aipa CLI (plain terminal, no TUI)."""
|
|
2
|
+
|
|
3
|
+
from .user_settings import load_settings, save_settings
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
_DEFAULT_BASE_URL = "https://openrouter.ai/api/v1"
|
|
7
|
+
_DEFAULT_MODEL = "openai/gpt-oss-120b:free"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def cmd_setup() -> None:
|
|
11
|
+
"""Run interactive setup and save settings."""
|
|
12
|
+
current = load_settings()
|
|
13
|
+
|
|
14
|
+
print("AIPriceAction Terminal Setup")
|
|
15
|
+
print("=" * 30)
|
|
16
|
+
|
|
17
|
+
# Language
|
|
18
|
+
lang = _prompt(
|
|
19
|
+
"Language (en/vn)",
|
|
20
|
+
current.get("language", "en"),
|
|
21
|
+
)
|
|
22
|
+
if lang not in ("en", "vn"):
|
|
23
|
+
print("Invalid language, defaulting to 'en'.")
|
|
24
|
+
lang = "en"
|
|
25
|
+
|
|
26
|
+
# Reference ticker
|
|
27
|
+
ticker = _prompt(
|
|
28
|
+
"Reference ticker",
|
|
29
|
+
current.get("ticker", "VNINDEX"),
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# API key (optional)
|
|
33
|
+
api_key = _prompt(
|
|
34
|
+
"API key (press Enter to skip)",
|
|
35
|
+
current.get("api_key", ""),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Base URL
|
|
39
|
+
base_url = _prompt(
|
|
40
|
+
"Base URL",
|
|
41
|
+
current.get("openai_base_url") or _DEFAULT_BASE_URL,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Model
|
|
45
|
+
model = _prompt(
|
|
46
|
+
"Model",
|
|
47
|
+
current.get("openai_model") or _DEFAULT_MODEL,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
data = {
|
|
51
|
+
"ticker": ticker.upper(),
|
|
52
|
+
"interval": current.get("interval", "1D"),
|
|
53
|
+
"language": lang,
|
|
54
|
+
"api_key": api_key,
|
|
55
|
+
"openai_base_url": base_url,
|
|
56
|
+
"openai_model": model,
|
|
57
|
+
"setup_done": True,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
save_settings(data)
|
|
61
|
+
print("Setup complete.")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _prompt(label: str, default: str) -> str:
|
|
65
|
+
"""Prompt user with current default shown in brackets."""
|
|
66
|
+
hint = f" [{default}]" if default else ""
|
|
67
|
+
try:
|
|
68
|
+
value = input(f"{label}{hint}: ").strip()
|
|
69
|
+
except EOFError:
|
|
70
|
+
return default
|
|
71
|
+
return value if value else default
|
|
@@ -151,8 +151,13 @@ class SettingsTab(Vertical):
|
|
|
151
151
|
self.app.language = language
|
|
152
152
|
from aipriceaction import AIContextBuilder
|
|
153
153
|
self.app.builder = AIContextBuilder(lang=language)
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
try:
|
|
155
|
+
from .agents import AgentSession, AgentConfig
|
|
156
|
+
self.app.agent = AgentSession(AgentConfig(lang=language))
|
|
157
|
+
self.app._agent_lang = language
|
|
158
|
+
except Exception:
|
|
159
|
+
self.app.agent = None
|
|
160
|
+
self.app._agent_lang = None
|
|
156
161
|
|
|
157
162
|
data: dict = {
|
|
158
163
|
"ticker": ticker,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Persistent user settings stored in ~/.aipriceaction/settings.json."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import os
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
6
7
|
_CONFIG_DIR = Path.home() / ".aipriceaction"
|
|
@@ -13,6 +14,7 @@ _DEFAULTS = {
|
|
|
13
14
|
"api_key": "",
|
|
14
15
|
"openai_base_url": "",
|
|
15
16
|
"openai_model": "",
|
|
17
|
+
"setup_done": False,
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
|
|
@@ -28,3 +30,21 @@ def save_settings(data: dict) -> None:
|
|
|
28
30
|
"""Persist settings to disk, creating the config directory if needed."""
|
|
29
31
|
_CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
30
32
|
_SETTINGS_FILE.write_text(json.dumps(data, indent=2))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def apply_settings_to_env() -> None:
|
|
36
|
+
"""Seed environment variables from settings.json.
|
|
37
|
+
|
|
38
|
+
Uses ``os.environ.setdefault`` so that existing env vars always win.
|
|
39
|
+
Must be called before any SDK import that reads env vars.
|
|
40
|
+
"""
|
|
41
|
+
settings = load_settings()
|
|
42
|
+
_mapping = {
|
|
43
|
+
"OPENAI_API_KEY": "api_key",
|
|
44
|
+
"OPENAI_BASE_URL": "openai_base_url",
|
|
45
|
+
"OPENAI_MODEL": "openai_model",
|
|
46
|
+
}
|
|
47
|
+
for env_key, settings_key in _mapping.items():
|
|
48
|
+
value = settings.get(settings_key, "")
|
|
49
|
+
if value:
|
|
50
|
+
os.environ.setdefault(env_key, value)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|