| """ |
| Instalira sve funkcije (filters, tools, actions) i kreira Custom Model Preset. |
| Poziva se automatski iz setup_endpoints.sh pri startu Space-a. |
| |
| Koristi: python3 install_functions.py <base_url> <token> |
| """ |
|
|
| import json |
| import urllib.request |
| import urllib.error |
| import sys |
| import os |
|
|
| BASE_URL = sys.argv[1] if len(sys.argv) > 1 else 'http://localhost:8080' |
| TOKEN = sys.argv[2] if len(sys.argv) > 2 else '' |
|
|
| if not TOKEN: |
| print("β No token provided.") |
| sys.exit(1) |
|
|
| HEADERS = { |
| 'Content-Type': 'application/json', |
| 'Authorization': f'Bearer {TOKEN}' |
| } |
|
|
| APP_DIR = os.path.dirname(os.path.abspath(__file__)) |
|
|
| |
| |
| |
|
|
| def api(method, path, data=None): |
| url = BASE_URL + path |
| body = json.dumps(data).encode() if data is not None else None |
| req = urllib.request.Request(url, data=body, headers=HEADERS, method=method) |
| try: |
| with urllib.request.urlopen(req) as r: |
| return r.status, json.loads(r.read().decode()) |
| except urllib.error.HTTPError as e: |
| return e.code, e.read().decode() |
|
|
| def install_function(func_id, name, func_type, content): |
| """Instaliraj ili aΕΎuriraj funkciju.""" |
| |
| status, _ = api('GET', f'/api/v1/functions/id/{func_id}') |
| |
| payload = { |
| 'id': func_id, |
| 'name': name, |
| 'type': func_type, |
| 'content': content, |
| 'meta': {'description': name} |
| } |
| |
| if status == 200: |
| s, r = api('POST', f'/api/v1/functions/id/{func_id}/update', payload) |
| label = "aΕΎuriran" if s == 200 else f"greΕ‘ka {s}" |
| else: |
| s, r = api('POST', '/api/v1/functions/create', payload) |
| label = "instaliran" if s == 200 else f"greΕ‘ka {s}" |
| |
| |
| api('POST', f'/api/v1/functions/id/{func_id}/toggle') |
| |
| icon = "β
" if s == 200 else "β" |
| print(f" {icon} [{func_type}] {name} β {label}") |
| return s == 200 |
|
|
| def load_json_function(filepath): |
| """UΔitaj funkciju iz JSON export fajla.""" |
| with open(filepath) as f: |
| d = json.load(f) |
| item = d[0] if isinstance(d, list) else d |
| |
| content = item.get('content', '') |
| if 'class Filter' in content: |
| ftype = 'filter' |
| elif 'class Pipe' in content: |
| ftype = 'pipe' |
| elif 'class Tools' in content or 'class Action' in content: |
| ftype = 'action' |
| else: |
| ftype = item.get('meta', {}).get('type', 'filter') |
| return item['id'], item['name'], ftype, content |
|
|
| |
| |
| |
|
|
| print("\nπ¦ Instaliram funkcije iz JSON fajlova...") |
|
|
| JSON_FILES = [ |
| 'global_system_prompt_filter.json', |
| '__easysearch_v0_4_3__high-performance_web_search_filter.json', |
| 'token_saver.json', |
| 'context_clip_filter.json', |
| 'auto_disable_native_tools.json', |
| 'auto_memory.json', |
| '__mermaid_doctor_-_heals_broken_mermaid_diagrams_generated_by_small_or_hallucinating_models.json', |
| 'github_simple.json', |
| 'pdf_tools_-_rich_ui_for_in_context_basic_editing_.json', |
| ] |
|
|
| installed_filter_ids = [] |
| for fname in JSON_FILES: |
| fpath = os.path.join(APP_DIR, fname) |
| if not os.path.exists(fpath): |
| print(f" β οΈ {fname} nije pronaΔen, preskaΔem") |
| continue |
| func_id, name, ftype, content = load_json_function(fpath) |
| ok = install_function(func_id, name, ftype, content) |
| if ok and ftype == 'filter': |
| installed_filter_ids.append(func_id) |
|
|
| |
| |
| |
|
|
| print("\nπ οΈ Instaliram custom funkcije...") |
|
|
| CUSTOM_FUNCTIONS = [ |
| ('context_detector', 'Context Detector', 'filter', 'context_detector.py'), |
| ('token_compressor', 'Token Compressor', 'filter', 'token_compressor.py'), |
| ('coding_fallback', 'π Smart Coding Router', 'pipe', 'coding_priority_pipe.py'), |
| ('smart_matcher', 'Smart Matcher', 'tool', 'smart_matcher.py'), |
| ] |
|
|
| for func_id, name, ftype, pyfile in CUSTOM_FUNCTIONS: |
| fpath = os.path.join(APP_DIR, pyfile) |
| if not os.path.exists(fpath): |
| print(f" β οΈ {pyfile} nije pronaΔen") |
| continue |
| content = open(fpath).read() |
| ok = install_function(func_id, name, ftype, content) |
| if ok and ftype == 'filter': |
| installed_filter_ids.append(func_id) |
|
|
| |
| |
| |
|
|
| print("\nπ Postavljam Global System Prompt...") |
|
|
| SYSTEM_PROMPT_FILE = os.path.join(APP_DIR, 'system_prompt.txt') |
| if os.path.exists(SYSTEM_PROMPT_FILE): |
| system_prompt = open(SYSTEM_PROMPT_FILE).read() |
| |
| |
| valve_payload = { |
| 'system_message': system_prompt, |
| 'timezone': 'Europe/Sarajevo', |
| 'skip_tags': [] |
| } |
| s, r = api('POST', '/api/v1/functions/id/global_system_prompt_filter/valves/update', valve_payload) |
| if s == 200: |
| print(" β
System prompt postavljen") |
| else: |
| print(f" β οΈ GreΕ‘ka pri postavljanju system prompta: {s}") |
| else: |
| print(" β οΈ system_prompt.txt nije pronaΔen") |
|
|
| |
| |
| |
|
|
| print("\nπ€ Kreiram Custom Model Preset...") |
|
|
| |
| ALL_FILTER_IDS = list(dict.fromkeys([ |
| 'global_system_prompt_filter', |
| 'easysearch', |
| 'token_saver', |
| 'context_clip_filter', |
| 'auto_disable_native_tools', |
| 'auto_memory', |
| 'context_detector', |
| 'token_compressor', |
| 'mermaid_doctor', |
| ] + installed_filter_ids)) |
|
|
| model_payload = { |
| 'id': 'nerdur_assistant', |
| 'name': 'π§ Nerdur Assistant (Full)', |
| 'base_model_id': 'coding_fallback.coding_fallback', |
| 'meta': { |
| 'description': 'Smart Coding Router sa svim filterima: web search, memory, token saver, context detector i viΕ‘e.', |
| 'profile_image_url': '/static/favicon.png', |
| 'tags': [{'name': 'custom'}, {'name': 'coding'}], |
| 'capabilities': { |
| 'vision': False, |
| 'citations': True, |
| 'usage': True, |
| } |
| }, |
| 'params': { |
| 'system': open(SYSTEM_PROMPT_FILE).read() if os.path.exists(SYSTEM_PROMPT_FILE) else '', |
| 'temperature': 0.2, |
| 'max_tokens': 4096, |
| }, |
| 'filter_ids': ALL_FILTER_IDS, |
| 'tool_ids': ['smart_matcher'], |
| 'action_ids': ['pdf_tools_rich_ui_editor'], |
| } |
|
|
| |
| status, _ = api('GET', '/api/v1/models/model?id=nerdur_assistant') |
| if status == 200: |
| s, r = api('POST', '/api/v1/models/model/update?id=nerdur_assistant', model_payload) |
| label = "aΕΎuriran" if s == 200 else f"greΕ‘ka {s}: {str(r)[:100]}" |
| else: |
| s, r = api('POST', '/api/v1/models/create', model_payload) |
| label = "kreiran" if s == 200 else f"greΕ‘ka {s}: {str(r)[:100]}" |
|
|
| icon = "β
" if s == 200 else "β" |
| print(f" {icon} Model 'π§ Nerdur Assistant (Full)' β {label}") |
|
|
| print("\nβ
Instalacija zavrΕ‘ena!") |
|
|
| |
| |
| |
|
|
| print("\nβ‘ Kreiram Gemini 2.5 Flash Preset...") |
|
|
| gemini_payload = { |
| 'id': 'nerdur_gemini_flash', |
| 'name': 'β‘ Gemini 2.5 Flash (Coding)', |
| 'base_model_id': 'models/gemini-2.5-flash', |
| 'meta': { |
| 'description': 'Gemini 2.5 Flash sa svim filterima β web search, memory, token saver i viΕ‘e.', |
| 'profile_image_url': '/static/favicon.png', |
| 'tags': [{'name': 'custom'}, {'name': 'coding'}, {'name': 'gemini'}], |
| 'capabilities': { |
| 'vision': True, |
| 'citations': True, |
| 'usage': True, |
| } |
| }, |
| 'params': { |
| 'system': open(SYSTEM_PROMPT_FILE).read() if os.path.exists(SYSTEM_PROMPT_FILE) else '', |
| 'temperature': 0.2, |
| 'max_tokens': 8192, |
| }, |
| 'filter_ids': ALL_FILTER_IDS, |
| 'tool_ids': ['smart_matcher'], |
| 'action_ids': ['pdf_tools_rich_ui_editor'], |
| } |
|
|
| status, _ = api('GET', '/api/v1/models/model?id=nerdur_gemini_flash') |
| if status == 200: |
| s, r = api('POST', '/api/v1/models/model/update?id=nerdur_gemini_flash', gemini_payload) |
| label = "aΕΎuriran" if s == 200 else f"greΕ‘ka {s}: {str(r)[:100]}" |
| else: |
| s, r = api('POST', '/api/v1/models/create', gemini_payload) |
| label = "kreiran" if s == 200 else f"greΕ‘ka {s}: {str(r)[:100]}" |
|
|
| icon = "β
" if s == 200 else "β" |
| print(f" {icon} Model 'β‘ Gemini 2.5 Flash (Coding)' β {label}") |
|
|
| print("\nβ
Svi modeli kreirani!") |
|
|
| |
| |
| |
|
|
| print("\nπ Kreiram Groq preset...") |
|
|
| groq_payload = { |
| 'id': 'nerdur_groq_coding', |
| 'name': 'π Groq Llama 3.3 70B (Coding)', |
| 'base_model_id': 'groq/llama-3.3-70b-versatile', |
| 'meta': { |
| 'description': 'Kimi K2 na Groq β izuzetno brz, odliΔan za kodiranje, besplatan.', |
| 'profile_image_url': '/static/favicon.png', |
| 'tags': [{'name': 'custom'}, {'name': 'coding'}, {'name': 'groq'}], |
| 'capabilities': {'vision': False, 'citations': True, 'usage': True} |
| }, |
| 'params': { |
| 'system': open(SYSTEM_PROMPT_FILE).read() if os.path.exists(SYSTEM_PROMPT_FILE) else '', |
| 'temperature': 0.2, |
| 'max_tokens': 8192, |
| }, |
| 'filter_ids': ALL_FILTER_IDS, |
| 'tool_ids': ['smart_matcher'], |
| 'action_ids': ['pdf_tools_rich_ui_editor'], |
| } |
|
|
| status, _ = api('GET', '/api/v1/models/model?id=nerdur_groq_coding') |
| if status == 200: |
| s, r = api('POST', '/api/v1/models/model/update?id=nerdur_groq_coding', groq_payload) |
| label = "aΕΎuriran" if s == 200 else f"greΕ‘ka {s}: {str(r)[:100]}" |
| else: |
| s, r = api('POST', '/api/v1/models/create', groq_payload) |
| label = "kreiran" if s == 200 else f"greΕ‘ka {s}: {str(r)[:100]}" |
|
|
| print(f" {'β
' if s == 200 else 'β'} 'π Groq Kimi K2 (Coding)' β {label}") |
|
|
| |
| |
| |
|
|
| print("\nπ§ Kreiram Cerebras preset...") |
|
|
| cerebras_payload = { |
| 'id': 'nerdur_cerebras_coding', |
| 'name': 'π§ Cerebras Llama 4 Scout (Coding)', |
| 'base_model_id': 'cerebras/llama-4-scout-17b-16e-instruct', |
| 'meta': { |
| 'description': 'Llama 3.3 70B na Cerebras β jedan od najbrΕΎih inference engine-a, besplatan.', |
| 'profile_image_url': '/static/favicon.png', |
| 'tags': [{'name': 'custom'}, {'name': 'coding'}, {'name': 'cerebras'}], |
| 'capabilities': {'vision': False, 'citations': True, 'usage': True} |
| }, |
| 'params': { |
| 'system': open(SYSTEM_PROMPT_FILE).read() if os.path.exists(SYSTEM_PROMPT_FILE) else '', |
| 'temperature': 0.2, |
| 'max_tokens': 8192, |
| }, |
| 'filter_ids': ALL_FILTER_IDS, |
| 'tool_ids': ['smart_matcher'], |
| 'action_ids': ['pdf_tools_rich_ui_editor'], |
| } |
|
|
| status, _ = api('GET', '/api/v1/models/model?id=nerdur_cerebras_coding') |
| if status == 200: |
| s, r = api('POST', '/api/v1/models/model/update?id=nerdur_cerebras_coding', cerebras_payload) |
| label = "aΕΎuriran" if s == 200 else f"greΕ‘ka {s}: {str(r)[:100]}" |
| else: |
| s, r = api('POST', '/api/v1/models/create', cerebras_payload) |
| label = "kreiran" if s == 200 else f"greΕ‘ka {s}: {str(r)[:100]}" |
|
|
| print(f" {'β
' if s == 200 else 'β'} 'π§ Cerebras Llama 3.3 (Coding)' β {label}") |
|
|
| |
| |
| |
|
|
| print("\nβ‘ Kreiram SambaNova preset...") |
|
|
| sambanova_payload = { |
| 'id': 'nerdur_sambanova_coding', |
| 'name': 'β‘ SambaNova Llama 3.3 (Coding)', |
| 'base_model_id': 'Meta-Llama-3.3-70B-Instruct', |
| 'meta': { |
| 'description': 'Llama 3.3 70B na SambaNova β brz inference, dobar za kodiranje, besplatan.', |
| 'profile_image_url': '/static/favicon.png', |
| 'tags': [{'name': 'custom'}, {'name': 'coding'}, {'name': 'sambanova'}], |
| 'capabilities': {'vision': False, 'citations': True, 'usage': True} |
| }, |
| 'params': { |
| 'system': open(SYSTEM_PROMPT_FILE).read() if os.path.exists(SYSTEM_PROMPT_FILE) else '', |
| 'temperature': 0.2, |
| 'max_tokens': 8192, |
| }, |
| 'filter_ids': ALL_FILTER_IDS, |
| 'tool_ids': ['smart_matcher'], |
| 'action_ids': ['pdf_tools_rich_ui_editor'], |
| } |
|
|
| status, _ = api('GET', '/api/v1/models/model?id=nerdur_sambanova_coding') |
| if status == 200: |
| s, r = api('POST', '/api/v1/models/model/update?id=nerdur_sambanova_coding', sambanova_payload) |
| label = "aΕΎuriran" if s == 200 else f"greΕ‘ka {s}: {str(r)[:100]}" |
| else: |
| s, r = api('POST', '/api/v1/models/create', sambanova_payload) |
| label = "kreiran" if s == 200 else f"greΕ‘ka {s}: {str(r)[:100]}" |
|
|
| print(f" {'β
' if s == 200 else 'β'} 'β‘ SambaNova Llama 3.3 (Coding)' β {label}") |
|
|
| print("\nπ Svih 5 preseta kreirano!") |
|
|
|
|
| |
| |
| |
|
|
| EXTRA_MODELS = [ |
| { |
| 'id': 'nerdur_claude', |
| 'name': 'π£ Claude Sonnet 4.5 (Coding)', |
| 'base_model_id': 'anthropic/claude-sonnet-4-5', |
| 'desc': 'Claude Sonnet 4.5 via Anthropic β odliΔan za kodiranje i analizu.', |
| 'max_tokens': 8192, |
| 'vision': True, |
| }, |
| { |
| 'id': 'nerdur_gpt4o', |
| 'name': 'π’ GPT-4o (Coding)', |
| 'base_model_id': 'openai/gpt-4o', |
| 'desc': 'OpenAI GPT-4o β multimodalan, odliΔan za kodiranje.', |
| 'max_tokens': 4096, |
| 'vision': True, |
| }, |
| { |
| 'id': 'nerdur_mistral', |
| 'name': 'π΅ Mistral Large (Coding)', |
| 'base_model_id': 'mistral-large-latest', |
| 'desc': 'Mistral Large β odliΔan europski model za kodiranje.', |
| 'max_tokens': 8192, |
| 'vision': False, |
| }, |
| { |
| 'id': 'nerdur_deepseek', |
| 'name': 'π΄ DeepSeek V3 (Coding)', |
| 'base_model_id': 'deepseek/deepseek-chat', |
| 'desc': 'DeepSeek V3 β izvrstan za kodiranje, vrlo jeftin.', |
| 'max_tokens': 8192, |
| 'vision': False, |
| }, |
| { |
| 'id': 'nerdur_xai', |
| 'name': 'β‘ Grok 3 (Coding)', |
| 'base_model_id': 'xai/grok-3', |
| 'desc': 'xAI Grok 3 β jak model sa web pristupom.', |
| 'max_tokens': 8192, |
| 'vision': True, |
| }, |
| { |
| 'id': 'nerdur_openrouter_free', |
| 'name': 'π OpenRouter Free (Coding)', |
| 'base_model_id': 'openrouter/meta-llama/llama-4-maverick:free', |
| 'desc': 'Llama 4 Maverick besplatno via OpenRouter.', |
| 'max_tokens': 8192, |
| 'vision': True, |
| }, |
| { |
| 'id': 'nerdur_fireworks', |
| 'name': 'π₯ Fireworks Llama 4 Scout (Coding)', |
| 'base_model_id': 'fireworks/accounts/fireworks/models/llama4-scout-instruct-basic', |
| 'desc': 'Llama 4 Scout na Fireworks β brz i jeftin.', |
| 'max_tokens': 8192, |
| 'vision': True, |
| }, |
| ] |
|
|
| system_prompt_text = open(SYSTEM_PROMPT_FILE).read() if os.path.exists(SYSTEM_PROMPT_FILE) else '' |
|
|
| print("\nπ Kreiram dodatne provider presete...") |
| for m in EXTRA_MODELS: |
| payload = { |
| 'id': m['id'], |
| 'name': m['name'], |
| 'base_model_id': m['base_model_id'], |
| 'meta': { |
| 'description': m['desc'], |
| 'profile_image_url': '/static/favicon.png', |
| 'tags': [{'name': 'custom'}, {'name': 'coding'}], |
| 'capabilities': { |
| 'vision': m['vision'], |
| 'citations': True, |
| 'usage': True, |
| } |
| }, |
| 'params': { |
| 'system': system_prompt_text, |
| 'temperature': 0.2, |
| 'max_tokens': m['max_tokens'], |
| }, |
| 'filter_ids': ALL_FILTER_IDS, |
| 'tool_ids': ['smart_matcher'], |
| 'action_ids': ['pdf_tools_rich_ui_editor'], |
| } |
| status, _ = api('GET', f'/api/v1/models/model?id={m["id"]}') |
| if status == 200: |
| s, r = api('POST', f'/api/v1/models/model/update?id={m["id"]}', payload) |
| label = "aΕΎuriran" if s == 200 else f"greΕ‘ka {s}: {str(r)[:80]}" |
| else: |
| s, r = api('POST', '/api/v1/models/create', payload) |
| label = "kreiran" if s == 200 else f"greΕ‘ka {s}: {str(r)[:80]}" |
| print(f" {'β
' if s == 200 else 'β'} {m['name']} β {label}") |
|
|
| print("\nπ Instalacija kompletna!") |
|
|