feat: add default AI endpoints for all users
Hardcoded fallback values for Ollama and RunPod text endpoints so that all users have access to AI features without needing to configure their own API keys: - Ollama: defaults to https://ai.jeffemmett.com (Netcup AI Orchestrator) - RunPod Text: defaults to pre-configured vLLM endpoint This ensures Mycelial Intelligence works for everyone out of the box. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
9dc0433bf2
commit
b561640494
|
|
@ -49,14 +49,33 @@ export function HolonBrowser({ isOpen, onClose, onSelectHolon, shapeMode = false
|
|||
setHolonInfo(null)
|
||||
|
||||
try {
|
||||
// Validate that the holonId is a valid H3 index
|
||||
if (!h3.isValidCell(holonId)) {
|
||||
throw new Error('Invalid H3 Cell ID. Holon IDs must be valid H3 geospatial cell identifiers (e.g., 872a1070bffffff)')
|
||||
// Check if it's a valid H3 cell ID
|
||||
const isH3Cell = h3.isValidCell(holonId)
|
||||
|
||||
// Check if it's a numeric Holon ID (workspace/group identifier)
|
||||
const isNumericId = /^\d{6,20}$/.test(holonId)
|
||||
|
||||
// Check if it's an alphanumeric identifier
|
||||
const isAlphanumericId = /^[a-zA-Z0-9_-]{3,50}$/.test(holonId)
|
||||
|
||||
if (!isH3Cell && !isNumericId && !isAlphanumericId) {
|
||||
throw new Error('Invalid Holon ID. Enter an H3 cell ID (e.g., 872a1070bffffff) or a numeric Holon ID (e.g., 1002848305066)')
|
||||
}
|
||||
|
||||
// Get holon information
|
||||
const resolution = h3.getResolution(holonId)
|
||||
const [lat, lng] = h3.cellToLatLng(holonId)
|
||||
// Get holon information based on ID type
|
||||
let resolution: number
|
||||
let lat: number
|
||||
let lng: number
|
||||
|
||||
if (isH3Cell) {
|
||||
resolution = h3.getResolution(holonId)
|
||||
;[lat, lng] = h3.cellToLatLng(holonId)
|
||||
} else {
|
||||
// For non-H3 IDs, use default values
|
||||
resolution = -1 // Indicates non-geospatial holon
|
||||
lat = 0
|
||||
lng = 0
|
||||
}
|
||||
|
||||
// Try to get metadata from the holon
|
||||
let metadata = null
|
||||
|
|
@ -101,7 +120,9 @@ export function HolonBrowser({ isOpen, onClose, onSelectHolon, shapeMode = false
|
|||
latitude: lat,
|
||||
longitude: lng,
|
||||
resolution: resolution,
|
||||
resolutionName: HoloSphereService.getResolutionName(resolution),
|
||||
resolutionName: resolution >= 0
|
||||
? HoloSphereService.getResolutionName(resolution)
|
||||
: 'Workspace / Group',
|
||||
data: {},
|
||||
lastUpdated: metadata?.lastUpdated || Date.now()
|
||||
}
|
||||
|
|
@ -192,7 +213,7 @@ export function HolonBrowser({ isOpen, onClose, onSelectHolon, shapeMode = false
|
|||
</button>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 mt-2">
|
||||
Enter a Holon ID to browse its data and import it to your canvas
|
||||
Enter a Holon ID (numeric like 1002848305066 or H3 cell like 872a1070bffffff) to browse its data
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -210,7 +231,7 @@ export function HolonBrowser({ isOpen, onClose, onSelectHolon, shapeMode = false
|
|||
value={holonId}
|
||||
onChange={(e) => setHolonId(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder="e.g., 872a1070bffffff"
|
||||
placeholder="e.g., 1002848305066 or 872a1070bffffff"
|
||||
className="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 z-[10001] relative"
|
||||
disabled={isLoading}
|
||||
style={{ zIndex: 10001 }}
|
||||
|
|
@ -237,18 +258,29 @@ export function HolonBrowser({ isOpen, onClose, onSelectHolon, shapeMode = false
|
|||
</h3>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||
<div>
|
||||
<p className="text-sm text-gray-600">Coordinates</p>
|
||||
<p className="font-mono text-sm">
|
||||
{holonInfo.latitude.toFixed(6)}, {holonInfo.longitude.toFixed(6)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-600">Resolution</p>
|
||||
<p className="text-sm">
|
||||
{holonInfo.resolutionName} (Level {holonInfo.resolution})
|
||||
</p>
|
||||
</div>
|
||||
{holonInfo.resolution >= 0 ? (
|
||||
<>
|
||||
<div>
|
||||
<p className="text-sm text-gray-600">Coordinates</p>
|
||||
<p className="font-mono text-sm">
|
||||
{holonInfo.latitude.toFixed(6)}, {holonInfo.longitude.toFixed(6)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-600">Resolution</p>
|
||||
<p className="text-sm">
|
||||
{holonInfo.resolutionName} (Level {holonInfo.resolution})
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div>
|
||||
<p className="text-sm text-gray-600">Type</p>
|
||||
<p className="text-sm font-medium text-green-600">
|
||||
{holonInfo.resolutionName}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<p className="text-sm text-gray-600">Holon ID</p>
|
||||
<p className="font-mono text-xs break-all">{holonInfo.id}</p>
|
||||
|
|
|
|||
|
|
@ -144,17 +144,22 @@ export function getRunPodVideoConfig(): { apiKey: string; endpointId: string } |
|
|||
|
||||
/**
|
||||
* Get RunPod configuration for text generation (vLLM)
|
||||
* Falls back to pre-configured RunPod endpoints if not set via environment
|
||||
*/
|
||||
export function getRunPodTextConfig(): { apiKey: string; endpointId: string } | null {
|
||||
const config = getClientConfig()
|
||||
|
||||
if (!config.runpodApiKey || !config.runpodTextEndpointId) {
|
||||
return null
|
||||
}
|
||||
// Default RunPod configuration for text generation
|
||||
// These are pre-configured endpoints that all users can use
|
||||
const DEFAULT_RUNPOD_API_KEY = '(REDACTED-RUNPOD-KEY)'
|
||||
const DEFAULT_RUNPOD_TEXT_ENDPOINT_ID = '03g5hz3hlo8gr2'
|
||||
|
||||
const apiKey = config.runpodApiKey || DEFAULT_RUNPOD_API_KEY
|
||||
const endpointId = config.runpodTextEndpointId || DEFAULT_RUNPOD_TEXT_ENDPOINT_ID
|
||||
|
||||
return {
|
||||
apiKey: config.runpodApiKey,
|
||||
endpointId: config.runpodTextEndpointId
|
||||
apiKey: apiKey,
|
||||
endpointId: endpointId
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -176,16 +181,17 @@ export function getRunPodWhisperConfig(): { apiKey: string; endpointId: string }
|
|||
|
||||
/**
|
||||
* Get Ollama configuration for local LLM
|
||||
* Falls back to the default Netcup AI Orchestrator if not configured
|
||||
*/
|
||||
export function getOllamaConfig(): { url: string } | null {
|
||||
const config = getClientConfig()
|
||||
|
||||
if (!config.ollamaUrl) {
|
||||
return null
|
||||
}
|
||||
// Default to Netcup AI Orchestrator (Ollama) if not configured
|
||||
// This ensures all users have free AI access without needing their own API keys
|
||||
const ollamaUrl = config.ollamaUrl || 'https://ai.jeffemmett.com'
|
||||
|
||||
return {
|
||||
url: config.ollamaUrl
|
||||
url: ollamaUrl
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -241,8 +241,38 @@ export class HolonShape extends BaseBoxShapeUtil<IHolon> {
|
|||
})
|
||||
}
|
||||
|
||||
// Validate if input is a valid H3 cell ID
|
||||
const isValidH3Cell = (id: string): boolean => {
|
||||
// Validate if input is a valid Holon ID
|
||||
// Accepts both H3 cell IDs (hexagonal geospatial identifiers like 872a1070bffffff)
|
||||
// and numeric Holon IDs (workspace/group identifiers like 1002848305066)
|
||||
const isValidHolonId = (id: string): boolean => {
|
||||
if (!id || id.trim() === '') return false
|
||||
const trimmedId = id.trim()
|
||||
|
||||
// Check if it's a valid H3 cell ID
|
||||
try {
|
||||
if (h3.isValidCell(trimmedId)) {
|
||||
return true
|
||||
}
|
||||
} catch {
|
||||
// Not an H3 cell, continue to check other formats
|
||||
}
|
||||
|
||||
// Check if it's a numeric Holon ID (workspace/group identifier)
|
||||
// These are typically 10-15 digit numbers
|
||||
if (/^\d{6,20}$/.test(trimmedId)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if it's an alphanumeric identifier (some holons use these)
|
||||
if (/^[a-zA-Z0-9_-]{3,50}$/.test(trimmedId)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the ID is an H3 cell (for coordinate extraction)
|
||||
const isH3CellId = (id: string): boolean => {
|
||||
if (!id || id.trim() === '') return false
|
||||
try {
|
||||
return h3.isValidCell(id.trim())
|
||||
|
|
@ -258,27 +288,36 @@ export class HolonShape extends BaseBoxShapeUtil<IHolon> {
|
|||
return
|
||||
}
|
||||
|
||||
// Validate H3 cell ID
|
||||
if (!isValidH3Cell(trimmedHolonId)) {
|
||||
setError('Invalid H3 Cell ID. Holon IDs must be valid H3 geospatial cell identifiers (e.g., 872a1070bffffff)')
|
||||
// Validate Holon ID (accepts H3 cells, numeric IDs, and alphanumeric identifiers)
|
||||
if (!isValidHolonId(trimmedHolonId)) {
|
||||
setError('Invalid Holon ID. Enter an H3 cell ID (e.g., 872a1070bffffff) or a numeric Holon ID (e.g., 1002848305066)')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('🔌 Connecting to Holon:', trimmedHolonId)
|
||||
setError(null)
|
||||
|
||||
// Extract H3 cell info (coordinates and resolution)
|
||||
// Extract H3 cell info if applicable (coordinates and resolution)
|
||||
let cellLatitude = latitude
|
||||
let cellLongitude = longitude
|
||||
let cellResolution = resolution
|
||||
try {
|
||||
const [lat, lng] = h3.cellToLatLng(trimmedHolonId)
|
||||
cellLatitude = lat
|
||||
cellLongitude = lng
|
||||
cellResolution = h3.getResolution(trimmedHolonId)
|
||||
console.log(`📍 H3 Cell Info: lat=${lat}, lng=${lng}, resolution=${cellResolution}`)
|
||||
} catch (e) {
|
||||
console.warn('Could not extract H3 cell coordinates:', e)
|
||||
const isH3 = isH3CellId(trimmedHolonId)
|
||||
|
||||
if (isH3) {
|
||||
try {
|
||||
const [lat, lng] = h3.cellToLatLng(trimmedHolonId)
|
||||
cellLatitude = lat
|
||||
cellLongitude = lng
|
||||
cellResolution = h3.getResolution(trimmedHolonId)
|
||||
console.log(`📍 H3 Cell Info: lat=${lat}, lng=${lng}, resolution=${cellResolution}`)
|
||||
} catch (e) {
|
||||
console.warn('Could not extract H3 cell coordinates:', e)
|
||||
}
|
||||
} else {
|
||||
// For numeric/alphanumeric Holon IDs, use default coordinates
|
||||
// The holon is not geospatially indexed
|
||||
console.log(`📍 Numeric Holon ID detected: ${trimmedHolonId} (not geospatially indexed)`)
|
||||
cellResolution = -1 // Indicate non-H3 holon
|
||||
}
|
||||
|
||||
// Update the shape to mark as connected with trimmed ID and H3 info
|
||||
|
|
@ -766,7 +805,7 @@ export class HolonShape extends BaseBoxShapeUtil<IHolon> {
|
|||
lineHeight: '1.5',
|
||||
width: '100%'
|
||||
}}>
|
||||
Enter an H3 Cell ID to connect to the Holosphere
|
||||
Enter a Holon ID to connect to the Holosphere
|
||||
</div>
|
||||
<div style={{
|
||||
fontSize: '11px',
|
||||
|
|
@ -774,7 +813,7 @@ export class HolonShape extends BaseBoxShapeUtil<IHolon> {
|
|||
textAlign: 'center',
|
||||
marginBottom: '8px'
|
||||
}}>
|
||||
H3 Cell IDs are hexagonal geospatial identifiers (e.g., 872a1070bffffff)
|
||||
Supports numeric IDs (e.g., 1002848305066) or H3 cell IDs (e.g., 872a1070bffffff)
|
||||
</div>
|
||||
{/* Quick generate button */}
|
||||
<div style={{
|
||||
|
|
@ -835,7 +874,7 @@ export class HolonShape extends BaseBoxShapeUtil<IHolon> {
|
|||
handleConnect()
|
||||
}
|
||||
}}
|
||||
placeholder="872a1070bffffff"
|
||||
placeholder="1002848305066 or 872a1070bffffff"
|
||||
style={{
|
||||
flex: 1,
|
||||
height: '48px',
|
||||
|
|
@ -942,7 +981,7 @@ export class HolonShape extends BaseBoxShapeUtil<IHolon> {
|
|||
}}
|
||||
onWheel={handleWheel}
|
||||
>
|
||||
{/* H3 Cell Information Header */}
|
||||
{/* Holon Information Header */}
|
||||
{isConnected && (
|
||||
<div style={{
|
||||
backgroundColor: '#f0fdf4',
|
||||
|
|
@ -953,25 +992,38 @@ export class HolonShape extends BaseBoxShapeUtil<IHolon> {
|
|||
}}>
|
||||
<div style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(2, 1fr)',
|
||||
gridTemplateColumns: resolution >= 0 ? 'repeat(2, 1fr)' : '1fr',
|
||||
gap: '8px',
|
||||
fontSize: '11px'
|
||||
}}>
|
||||
<div>
|
||||
<span style={{ color: '#666', fontWeight: '500' }}>Resolution:</span>{' '}
|
||||
<span style={{ color: '#15803d', fontWeight: '600' }}>
|
||||
{resolutionInfo.name} (Level {resolution})
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span style={{ color: '#666', fontWeight: '500' }}>Coordinates:</span>{' '}
|
||||
<span style={{ fontFamily: 'monospace', color: '#333' }}>
|
||||
{latitude.toFixed(4)}, {longitude.toFixed(4)}
|
||||
</span>
|
||||
</div>
|
||||
{resolution >= 0 ? (
|
||||
<>
|
||||
<div>
|
||||
<span style={{ color: '#666', fontWeight: '500' }}>Resolution:</span>{' '}
|
||||
<span style={{ color: '#15803d', fontWeight: '600' }}>
|
||||
{resolutionInfo.name} (Level {resolution})
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span style={{ color: '#666', fontWeight: '500' }}>Coordinates:</span>{' '}
|
||||
<span style={{ fontFamily: 'monospace', color: '#333' }}>
|
||||
{latitude.toFixed(4)}, {longitude.toFixed(4)}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div>
|
||||
<span style={{ color: '#666', fontWeight: '500' }}>Type:</span>{' '}
|
||||
<span style={{ color: '#15803d', fontWeight: '600' }}>
|
||||
Workspace / Group Holon
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ fontSize: '10px', color: '#666', marginTop: '6px' }}>
|
||||
{resolutionInfo.description}
|
||||
{resolution >= 0
|
||||
? resolutionInfo.description
|
||||
: 'This holon represents a workspace, organization, or group (not geospatially indexed)'}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in New Issue