Skip to content

Commit f69366c

Browse files
committed
Updated AI model selection
- Added many more models (Anthropic Claude, Google Gemini, Deepseek, local Ollama...) - Updated the default to GPT 4.1
1 parent 9710af9 commit f69366c

2 files changed

Lines changed: 163 additions & 59 deletions

File tree

src/qualcoder/__main__.py

Lines changed: 9 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
from qualcoder.view_graph import ViewGraph
8585
from qualcoder.view_image import DialogCodeImage
8686
from qualcoder.ai_prompts import DialogAiEditPrompts
87+
from qualcoder.ai_llm import get_default_ai_models, update_ai_models
8788

8889
# Check if VLC installed, for warning message for code_av
8990
vlc = None
@@ -629,7 +630,7 @@ def write_config_ini(self, settings, ai_models):
629630
config['DEFAULT'] = settings
630631
# add AI models
631632
if len(ai_models) == 0:
632-
ai_models = self.ai_models_create_defaults()
633+
ai_models = get_default_ai_models()
633634
for model in ai_models:
634635
model_section = 'ai_model_' + model['name']
635636
config[model_section] = {}
@@ -659,7 +660,7 @@ def _load_config_ini(self):
659660
msg = _("Cannot load config.ini.\nCharacter decoding error.\nUsing QualCoder default settings.")
660661
print(msg)
661662
Message(self, _("Cannot load config.ini file"), msg).exec()
662-
return self.default_settings, self.ai_models_create_defaults()
663+
return self.default_settings, get_default_ai_models()
663664

664665
if 'fontsize' in default:
665666
result['fontsize'] = default.getint('fontsize')
@@ -696,58 +697,12 @@ def _load_config_ini(self):
696697
}
697698
ai_models.append(model)
698699
if len(ai_models) == 0: # no models loaded, create default
699-
ai_models = self.ai_models_create_defaults()
700+
ai_models = get_default_ai_models()
701+
else:
702+
current_ai_model_index = int(result.get('ai_model_index', -1))
703+
ai_models, result['ai_model_index'] = update_ai_models(ai_models, current_ai_model_index)
700704
return result, ai_models
701705

702-
def ai_models_create_defaults(self):
703-
"""Returns a list of the default AI model parameters
704-
"""
705-
models = [
706-
{
707-
'name': 'OpenAI_GPT4o',
708-
'desc': """Current default model from OpenAI, faster and cheaper than GPT4-turbo.
709-
You need an API-key from OpenAI and have paid for credits in your account.
710-
OpenAI will charge a small amount for every use.""",
711-
'access_info_url': 'https://platform.openai.com/api-keys',
712-
'large_model': 'gpt-4o',
713-
'large_model_context_window': '128000',
714-
'fast_model': 'gpt-4o-mini',
715-
'fast_model_context_window': '128000',
716-
'api_base': '',
717-
'api_key': ''
718-
},
719-
{
720-
'name': 'GPT-4-turbo',
721-
'desc': """Classic model from OpenAI, still very capable.
722-
You need an API-key from OpenAI and have paid for credits in your account.
723-
OpenAI will charge a small amount for every use.""",
724-
'access_info_url': 'https://platform.openai.com/api-keys',
725-
'large_model': 'gpt-4-turbo',
726-
'large_model_context_window': '128000',
727-
'fast_model': 'gpt-4o-mini',
728-
'fast_model_context_window': '128000',
729-
'api_base': '',
730-
'api_key': ''
731-
},
732-
{
733-
'name': 'Blablador',
734-
'desc': """A free and open source model, excellent privacy,
735-
but not as powerful as GPT-4.
736-
Blablador is free to use and runs on a server of the Helmholtz Society,
737-
a large non-profit research organization in Germany. To gain
738-
access and get an API-key, you have to identify yourself once with your
739-
university, ORCID, GitHub, or Google account.""",
740-
'access_info_url': 'https://sdlaml.pages.jsc.fz-juelich.de/ai/guides/blablador_api_access/',
741-
'large_model': 'alias-large',
742-
'large_model_context_window': '32768',
743-
'fast_model': 'alias-fast',
744-
'fast_model_context_window': '32768',
745-
'api_base': 'https://helmholtz-blablador.fz-juelich.de:8000/v1',
746-
'api_key': ''
747-
}
748-
]
749-
return models
750-
751706
def check_and_add_additional_settings(self, settings_data, ai_models):
752707
""" Newer features include width and height settings for many dialogs and main window.
753708
timestamp format.
@@ -817,7 +772,7 @@ def check_and_add_additional_settings(self, settings_data, ai_models):
817772

818773
# Check AI models
819774
if len(ai_models) == 0: # No models loaded, create default
820-
ai_models = self.ai_models_create_defaults()
775+
ai_models = get_default_ai_models()
821776

822777
# Write out new ini file, if needed
823778
if len(settings_data) > dict_len:
@@ -1011,7 +966,7 @@ def load_settings(self):
1011966
if (not len(result) or 'codername' not in result.keys() or 'stylesheet' not in result.keys() or
1012967
'speakernameformat' not in result.keys()):
1013968
# create default:
1014-
ai_models = self.ai_models_create_defaults()
969+
ai_models = get_default_ai_models()
1015970
self.write_config_ini(self.default_settings, ai_models)
1016971
logger.info('Initialized config.ini')
1017972
result, ai_models = self._load_config_ini()

src/qualcoder/ai_llm.py

Lines changed: 154 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import fuzzysearch
4646
import json_repair
4747
import asyncio
48+
import configparser
4849

4950
max_memo_length = 1500 # Maximum length of the memo send to the AI
5051

@@ -82,11 +83,159 @@ def get_available_models(api_base: str, api_key: str) -> list:
8283
if api_base == '':
8384
api_base = None
8485
client = OpenAI(api_key=api_key, base_url=api_base)
85-
response = client.models.list(timeout=6.0)
86+
response = client.models.list(timeout=4.0)
8687
model_dict = response.model_dump().get('data', [])
8788
model_list = sorted([model['id'] for model in model_dict])
8889
return model_list
8990

91+
def get_default_ai_models():
92+
ini_string = """
93+
[ai_model_OpenAI GPT4.1]
94+
desc = Powerful and large model from OpenAI, for complex tasks.
95+
You need an API-key from OpenAI and have paid for credits in your account.
96+
OpenAI will charge a small amount for every use.
97+
access_info_url = https://platform.openai.com/api-keys
98+
large_model = gpt-4.1
99+
large_model_context_window = 1000000
100+
fast_model = gpt-4.1-mini
101+
fast_model_context_window = 128000
102+
api_base =
103+
api_key =
104+
105+
[ai_model_OpenAI_GPT4o]
106+
desc = General use model from OpenAI, faster and cheaper than other options.
107+
You need an API-key from OpenAI and have paid for credits in your account.
108+
OpenAI will charge a small amount for every use.
109+
access_info_url = https://platform.openai.com/api-keys
110+
large_model = gpt-4o
111+
large_model_context_window = 1000000
112+
fast_model = gpt-4o-mini
113+
fast_model_context_window = 128000
114+
api_base =
115+
api_key =
116+
117+
[ai_model_Blablador]
118+
desc = Free and open source models, excellent privacy, but not as powerful
119+
as the commercial offerings. Blablador runs on a server of the Helmholtz
120+
Society, a large non-profit research organization in Germany. To gain
121+
access and get an API-key, you have to identify yourself once with your
122+
university, ORCID, GitHub, or Google account.
123+
access_info_url = https://sdlaml.pages.jsc.fz-juelich.de/ai/guides/blablador_api_access/
124+
large_model = alias-large
125+
large_model_context_window = 128000
126+
fast_model = alias-fast
127+
fast_model_context_window = 32000
128+
api_base = https://helmholtz-blablador.fz-juelich.de:8000/v1
129+
api_key =
130+
131+
[ai_model_Blablador Huge]
132+
desc = Llama 3.1 405b, very large and powerful. Availability might change.
133+
Blablador is free to use and runs on a server of the Helmholtz Society,
134+
a large non-profit research organization in Germany. To gain
135+
access and get an API-key, you have to identify yourself once with your
136+
university, ORCID, GitHub, or Google account.
137+
access_info_url = https://sdlaml.pages.jsc.fz-juelich.de/ai/guides/blablador_api_access/
138+
large_model = alias-llama3-huge
139+
large_model_context_window = 128000
140+
fast_model = alias-fast
141+
fast_model_context_window = 128000
142+
api_base = https://helmholtz-blablador.fz-juelich.de:8000/v1
143+
api_key =
144+
145+
[ai_model_Anthropic Claude]
146+
desc = Claude is a family of high quality models from Anthropic.
147+
You need an API-key from Anthropic and credits in your account.
148+
Anthropic will charge a small amount for every use.
149+
access_info_url = https://console.anthropic.com/settings/keys
150+
large_model = claude-opus-4-20250514
151+
large_model_context_window = 200000
152+
fast_model = claude-sonnet-4-20250514
153+
fast_model_context_window = 200000
154+
api_base = https://api.anthropic.com/v1/
155+
api_key =
156+
157+
[ai_model_Google Gemini]
158+
desc = Google offers several free and paid models on their servers.
159+
Select one in the Advanced AI options below.
160+
You need an API-key from Google.
161+
access_info_url = https://ai.google.dev/gemini-api/docs
162+
large_model = gemini-2.5-flash
163+
large_model_context_window = 1000000
164+
fast_model = gemini-2.5-flash
165+
fast_model_context_window = 1000000
166+
api_base = https://generativelanguage.googleapis.com/v1beta/openai/
167+
api_key =
168+
169+
[ai_model_Deepseek Chat V3]
170+
desc = Deepseek is a high quality Chinese chat model.
171+
You will need an an API-key from Deepseek and have payed credits in your account.
172+
Deepseek will charge a small amount for every use.
173+
access_info_url = https://platform.deepseek.com/api_keys
174+
large_model = deepseek-chat
175+
large_model_context_window = 64000
176+
fast_model = deepseek-chat
177+
fast_model_context_window = 64000
178+
api_base = https://api.deepseek.com
179+
api_key =
180+
181+
[ai_model_OpenRouter]
182+
desc = OpenRouter is a unified interface to access many different AI language
183+
models, both free and paid. You need an API-key from OpenRouter.
184+
Select a model in the Advanced AI Options below.
185+
access_info_url = https://openrouter.ai/
186+
large_model = deepseek/deepseek-chat:free
187+
large_model_context_window = 64000
188+
fast_model = deepseek/deepseek-chat:free
189+
fast_model_context_window = 64000
190+
api_base = https://openrouter.ai/api/v1
191+
api_key =
192+
193+
[ai_model_Ollama local AI]
194+
desc = Ollama is an open source server that lets you run LLMs locally on
195+
your computer. To use it in QualCoder, you must have Ollama set up and
196+
running first. Use the Advanced AI Options below to select between your
197+
locally installed models.
198+
access_info_url = https://ollama.com
199+
large_model = test
200+
large_model_context_window = 1
201+
fast_model = test
202+
fast_model_context_window = 1
203+
api_base = http://localhost:11434/v1/
204+
api_key = <no API key needed>
205+
"""
206+
207+
config = configparser.ConfigParser()
208+
config.read_string(ini_string)
209+
ai_models = []
210+
for section in config.sections():
211+
if section.startswith('ai_model_'):
212+
model = {
213+
'name': section[9:],
214+
'desc': config[section].get('desc', ''),
215+
'access_info_url': config[section].get('access_info_url', ''),
216+
'large_model': config[section].get('large_model', ''),
217+
'large_model_context_window': config[section].get('large_model_context_window', '32768'),
218+
'fast_model': config[section].get('fast_model', ''),
219+
'fast_model_context_window': config[section].get('fast_model_context_window', '32768'),
220+
'api_base': config[section].get('api_base', ''),
221+
'api_key': config[section].get('api_key', '')
222+
}
223+
ai_models.append(model)
224+
return ai_models
225+
226+
def update_ai_models(current_models: list, current_model_index: int) -> tuple[list, int]:
227+
default_models = get_default_ai_models()
228+
current_models_names = {model['name'] for model in current_models}
229+
for model in default_models:
230+
if not model['name'] in current_models_names:
231+
if model['name'] == 'OpenAI GPT4.1': # insert this at the top, because it is the current default model
232+
current_models.insert(0, model)
233+
if current_model_index >= 0:
234+
current_model_index += 1
235+
else:
236+
current_models.append(model)
237+
return current_models, current_model_index
238+
90239
class AiLLM():
91240
""" This manages the communication between qualcoder, the vectorstore
92241
and the LLM (large language model, e.g. GPT-4)."""
@@ -150,7 +299,7 @@ def init_llm(self, main_window, rebuild_vectorstore=False, enable_ai=False):
150299
if int(self.app.settings['ai_model_index']) >= len(self.app.ai_models): # model index out of range
151300
self.app.settings['ai_model_index'] = -1
152301
if int(self.app.settings['ai_model_index']) < 0:
153-
msg = _('AI: Please set up the AI model')
302+
msg = _('AI: In the follwoing window, please set up the AI model.')
154303
Message(self.app, _('AI Setup'), msg).exec()
155304

156305
main_window.change_settings(section='AI', enable_ai=True)
@@ -167,13 +316,15 @@ def init_llm(self, main_window, rebuild_vectorstore=False, enable_ai=False):
167316
curr_model = self.app.ai_models[int(self.app.settings['ai_model_index'])]
168317

169318
large_model = curr_model['large_model']
319+
if large_model.find('gpt-4-turbo') > -1:
320+
self.parent_text_edit.append(_('AI: You are still using the outdated GPT-4 turbo. Consider switching to a newer model, such as GPT 4.1. Go to Project > Settings to change the AI profile and model.'))
170321
self.large_llm_context_window = int(curr_model['large_model_context_window'])
171322
fast_model = curr_model['fast_model']
172323
self.fast_llm_context_window = int(curr_model['fast_model_context_window'])
173324
api_base = curr_model['api_base']
174325
api_key = curr_model['api_key']
175326
if api_key == '':
176-
msg = _('Please enter an API-key for the AI in the following dialog (or "None" if not API-key is needed).')
327+
msg = _('Please enter an API-key for the AI in the following dialog.')
177328
Message(self.app, _('AI API-key'), msg).exec()
178329
main_window.change_settings(section='AI', enable_ai=True)
179330
curr_model = self.app.ai_models[int(self.app.settings['ai_model_index'])]
@@ -187,8 +338,6 @@ def init_llm(self, main_window, rebuild_vectorstore=False, enable_ai=False):
187338
# Success, API-key was set. But since the "change_settings" function will start
188339
# a new "init_llm" anyways, we are going to quit here
189340
return
190-
#elif api_key == 'None':
191-
# api_key = ''
192341
if large_model == '' or fast_model == '':
193342
msg = _('In the following dialog, go to "Advanced AI Options" and select a large and a fast AI model (both can be the same).')
194343
Message(self.app, _('AI Model Selection'), msg).exec()

0 commit comments

Comments
 (0)