136 lines
5.2 KiB
Plaintext
136 lines
5.2 KiB
Plaintext
---
|
|
description: Use the Fusion API module for all API calls (OpenAI, Anthropic, Google Maps, Twilio, OAuth) in any Fusion module
|
|
globs: fusion_*/models/**/*.py, fusion_*/services/**/*.py
|
|
alwaysApply: false
|
|
---
|
|
|
|
# Fusion API Integration Guide
|
|
|
|
When any Fusion module needs to call an external API (OpenAI, Anthropic, Google Maps, Twilio, Google/Microsoft OAuth), it MUST use the centralized `fusion.api.service` instead of managing its own keys.
|
|
|
|
The service lives at `fusion_api/models/api_service.py` and is an AbstractModel accessible via `self.env['fusion.api.service']`.
|
|
|
|
## Available Methods
|
|
|
|
### 1. `call_openai()` -- AI text generation via OpenAI
|
|
|
|
```python
|
|
result = self.env['fusion.api.service'].call_openai(
|
|
consumer='fusion_clock_ai', # your module's technical name
|
|
feature='timesheet_summary', # descriptive feature name for tracking
|
|
messages=[
|
|
{'role': 'system', 'content': 'You are a helpful assistant.'},
|
|
{'role': 'user', 'content': 'Summarize this timesheet...'},
|
|
],
|
|
model='gpt-4o-mini', # optional, defaults to gpt-4o-mini
|
|
max_tokens=1024, # optional, defaults to 1024
|
|
user=self.env.user, # optional, defaults to current user
|
|
)
|
|
# Returns: str (the text response)
|
|
```
|
|
|
|
### 2. `call_anthropic()` -- AI text generation via Claude
|
|
|
|
```python
|
|
result = self.env['fusion.api.service'].call_anthropic(
|
|
consumer='fusion_notes',
|
|
feature='voice_transcription',
|
|
messages=[
|
|
{'role': 'user', 'content': 'Transcribe this text...'},
|
|
],
|
|
system='You are a medical transcription assistant.', # optional system prompt
|
|
model='claude-sonnet-4-20250514', # optional, defaults to claude-sonnet-4-20250514
|
|
max_tokens=1024, # optional
|
|
)
|
|
# Returns: str (the text response)
|
|
```
|
|
|
|
### 3. `get_api_key()` -- raw API key for non-AI services
|
|
|
|
```python
|
|
api_key = self.env['fusion.api.service'].get_api_key(
|
|
provider_type='google_maps', # one of: google_maps, twilio, custom
|
|
consumer='fusion_shipping', # your module's technical name
|
|
feature='geocoding', # optional feature name
|
|
)
|
|
# Returns: str (the raw API key to use in your own HTTP calls)
|
|
```
|
|
|
|
### 4. `get_oauth_credentials()` -- OAuth tokens for Google/Microsoft
|
|
|
|
```python
|
|
creds = self.env['fusion.api.service'].get_oauth_credentials(
|
|
provider_type='google_oauth', # google_oauth or microsoft_oauth
|
|
consumer='fusion_schedule',
|
|
feature='calendar_sync',
|
|
)
|
|
# Returns: dict with keys:
|
|
# client_id, client_secret, access_token, refresh_token, token_expiry, redirect_uri
|
|
```
|
|
|
|
## Provider Types
|
|
|
|
| Type | Use For |
|
|
|------|---------|
|
|
| `openai` | GPT-4o, GPT-4o-mini, o1, etc. |
|
|
| `anthropic` | Claude Sonnet, Haiku, Opus |
|
|
| `google_maps` | Geocoding, Places, Distance Matrix |
|
|
| `google_oauth` | Google Calendar, Drive OAuth |
|
|
| `microsoft_oauth` | Microsoft Calendar, Graph OAuth |
|
|
| `twilio` | SMS, Voice |
|
|
| `custom` | Any other API provider |
|
|
|
|
## What Happens Automatically
|
|
|
|
- The calling module is auto-registered as a consumer in Fusion API
|
|
- Every call is logged with: tokens, cost, response time, user, feature, model
|
|
- Budget caps (monthly/daily) are enforced before each call
|
|
- Rate limits (RPM/RPD) are enforced before each call
|
|
- Per-user limits are checked if configured
|
|
- On limit exceeded, a `UserError` is raised with a clear message
|
|
|
|
## Phased Migration Pattern
|
|
|
|
Do NOT add `fusion_api` as a hard dependency. Use a try/fallback pattern so the module works with or without Fusion API installed:
|
|
|
|
```python
|
|
def _call_ai(self, messages, feature='general'):
|
|
"""Call OpenAI through Fusion API, falling back to own key."""
|
|
try:
|
|
return self.env['fusion.api.service'].call_openai(
|
|
consumer='fusion_clock_ai',
|
|
feature=feature,
|
|
messages=messages,
|
|
)
|
|
except Exception:
|
|
api_key = self.env['ir.config_parameter'].sudo().get_param(
|
|
'fusion_clock_ai.openai_api_key'
|
|
)
|
|
if not api_key:
|
|
raise
|
|
from openai import OpenAI
|
|
client = OpenAI(api_key=api_key)
|
|
response = client.chat.completions.create(
|
|
model='gpt-4o-mini', messages=messages,
|
|
)
|
|
return response.choices[0].message.content
|
|
```
|
|
|
|
## Rules
|
|
|
|
- NEVER hardcode API keys in Python code
|
|
- NEVER store API keys in `ir.config_parameter` for new modules -- use Fusion API
|
|
- ALWAYS pass the module's technical name (e.g. `fusion_clock_ai`) as the `consumer` parameter
|
|
- ALWAYS pass a descriptive `feature` string for usage tracking granularity
|
|
- NEVER catch and silently swallow errors from `fusion.api.service` -- let UserError propagate
|
|
- DO NOT add `fusion_api` to `depends` in `__manifest__.py` -- use the fallback pattern above
|
|
- The `consumer` string should match the module's technical name exactly (folder name)
|
|
|
|
## Odoo 19 Conventions
|
|
|
|
- This project targets Odoo 19 (Community/Enterprise)
|
|
- No hardcoded colors in views or SCSS -- let Odoo handle dark/light theming
|
|
- Use `res.groups.privilege` for security groups (not `category_id`)
|
|
- Do not use deprecated `ir.cron` fields (`numbercall`, `doall`)
|
|
- Settings views: use `name=` attribute on `<app>` tags, not `data-key=`
|