Skip to content

Commit 32e1ab9

Browse files
Improved enterprise IT (#497)
1 parent 7a498d5 commit 32e1ab9

File tree

9 files changed

+200
-21
lines changed

9 files changed

+200
-21
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Things we are currently working on:
3434
- [x] ~~Add support for other languages (I18N) to AI Studio ([PR #381](https://github.com/MindWorkAI/AI-Studio/pull/381), [PR #400](https://github.com/MindWorkAI/AI-Studio/pull/400), [PR #404](https://github.com/MindWorkAI/AI-Studio/pull/404), [PR #429](https://github.com/MindWorkAI/AI-Studio/pull/429), [PR #446](https://github.com/MindWorkAI/AI-Studio/pull/446), [PR #451](https://github.com/MindWorkAI/AI-Studio/pull/451), [PR #455](https://github.com/MindWorkAI/AI-Studio/pull/455), [PR #458](https://github.com/MindWorkAI/AI-Studio/pull/458), [PR #462](https://github.com/MindWorkAI/AI-Studio/pull/462), [PR #469](https://github.com/MindWorkAI/AI-Studio/pull/469), [PR #486](https://github.com/MindWorkAI/AI-Studio/pull/486))~~
3535
- [x] ~~Add an I18N assistant to translate all AI Studio texts to a certain language & culture ([PR #422](https://github.com/MindWorkAI/AI-Studio/pull/422))~~
3636
- [x] ~~Provide MindWork AI Studio in German ([PR #430](https://github.com/MindWorkAI/AI-Studio/pull/430), [PR #446](https://github.com/MindWorkAI/AI-Studio/pull/446), [PR #451](https://github.com/MindWorkAI/AI-Studio/pull/451), [PR #455](https://github.com/MindWorkAI/AI-Studio/pull/455), [PR #458](https://github.com/MindWorkAI/AI-Studio/pull/458), [PR #462](https://github.com/MindWorkAI/AI-Studio/pull/462), [PR #469](https://github.com/MindWorkAI/AI-Studio/pull/469), [PR #486](https://github.com/MindWorkAI/AI-Studio/pull/486))~~
37-
- [x] ~~Add configuration plugins, which allow pre-defining some LLM providers in organizations ([PR #491](https://github.com/MindWorkAI/AI-Studio/pull/491), [PR #493](https://github.com/MindWorkAI/AI-Studio/pull/493), [PR #494](https://github.com/MindWorkAI/AI-Studio/pull/494))~~
37+
- [x] ~~Add configuration plugins, which allow pre-defining some LLM providers in organizations ([PR #491](https://github.com/MindWorkAI/AI-Studio/pull/491), [PR #493](https://github.com/MindWorkAI/AI-Studio/pull/493), [PR #494](https://github.com/MindWorkAI/AI-Studio/pull/494), [PR #497](https://github.com/MindWorkAI/AI-Studio/pull/497))~~
3838
- [ ] Add an app store for plugins, showcasing community-contributed plugins from public GitHub and GitLab repositories. This will enable AI Studio users to discover, install, and update plugins directly within the platform.
3939
- [ ] Add assistant plugins
4040

@@ -123,6 +123,9 @@ Stay tuned for more updates and enhancements to make MindWork AI Studio even mor
123123
## Building
124124
You want to know how to build MindWork AI Studio from source? [Check out the instructions here](documentation/Build.md).
125125

126+
## Enterprise IT
127+
Do you want to manage AI Studio centrally from your IT department? Yes, that’s possible. [Here’s how it works.](documentation/Enterprise%20IT.md)
128+
126129
## License
127130
MindWork AI Studio is licensed under the `FSL-1.1-MIT` license (functional source license). Here’s a simple rundown of what that means for you:
128131
- **Permitted Use**: Feel free to use, copy, modify, and share the software for your own projects, educational purposes, research, or even in professional services. The key is to use it in a way that doesn't compete with our offerings.

app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,7 +1129,7 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::REWRITEIMPROVE::ASSISTANTREWRITEIMPROVE::
11291129
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::REWRITEIMPROVE::ASSISTANTREWRITEIMPROVE::T3032662264"] = "Benutzerdefinierte Sprache"
11301130

11311131
-- Your input to improve
1132-
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::REWRITEIMPROVE::ASSISTANTREWRITEIMPROVE::T3037449423"] = "Ihr Vorschlag zur Verbesserung"
1132+
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::REWRITEIMPROVE::ASSISTANTREWRITEIMPROVE::T3037449423"] = "Ihr Text, der verbessert werden soll"
11331133

11341134
-- Writing style
11351135
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::REWRITEIMPROVE::ASSISTANTREWRITEIMPROVE::T3754048862"] = "Schreibstil"
@@ -4213,7 +4213,7 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2173617769"] = "Diese Bibliothek wird
42134213
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2174764529"] = "Für die sichere Kommunikation zwischen der Benutzeroberfläche und der Laufzeit müssen wir Zertifikate erstellen. Diese Rust-Bibliothek eignet sich hervorragend dafür."
42144214

42154215
-- AI Studio runs without an enterprise configuration.
4216-
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2244723851"] = "AI Studio läuft ohne eine Konfiguration ihrer Organisation."
4216+
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2244723851"] = "Dies ist eine private AI Studio-Installation. Es wird keine Konfiguration einer Organisation verwendet."
42174217

42184218
-- OK
42194219
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2246359087"] = "OK"

app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4213,7 +4213,7 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2173617769"] = "This library is used t
42134213
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2174764529"] = "For the secure communication between the user interface and the runtime, we need to create certificates. This Rust library is great for this purpose."
42144214

42154215
-- AI Studio runs without an enterprise configuration.
4216-
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2244723851"] = "AI Studio runs without an enterprise configuration."
4216+
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2244723851"] = "This is a private AI Studio installation. It runs without an enterprise configuration."
42174217

42184218
-- OK
42194219
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2246359087"] = "OK"

app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Download.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public static async Task<bool> TryDownloadingConfigPluginAsync(Guid configPlugId
4242
var tempDownloadFile = Path.GetTempFileName();
4343
try
4444
{
45+
await LockHotReloadAsync();
4546
using var httpClient = new HttpClient();
4647
var response = await httpClient.GetAsync(downloadUrl, cancellationToken);
4748
if (response.IsSuccessStatusCode)
@@ -80,6 +81,8 @@ public static async Task<bool> TryDownloadingConfigPluginAsync(Guid configPlugId
8081
LOG.LogError(e, "Failed to delete the temporary download file.");
8182
}
8283
}
84+
85+
UnlockHotReload();
8386
}
8487

8588
return true;

app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.HotReload.cs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,41 @@ private static async void HotReloadEventHandler(object _, FileSystemEventArgs ar
4646
try
4747
{
4848
LOG.LogInformation($"File changed ({changeType}): {args.FullPath}. Reloading plugins...");
49-
50-
// Wait for parallel writes to finish:
51-
await Task.Delay(TimeSpan.FromSeconds(3));
49+
if (File.Exists(HOT_RELOAD_LOCK_FILE))
50+
{
51+
LOG.LogInformation("Hot reload lock file exists. Waiting for it to be released before proceeding with the reload.");
52+
53+
var lockFileCancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
54+
var token = lockFileCancellationTokenSource.Token;
55+
var waitTime = TimeSpan.FromSeconds(1);
56+
while (File.Exists(HOT_RELOAD_LOCK_FILE) && !token.IsCancellationRequested)
57+
{
58+
try
59+
{
60+
LOG.LogDebug("Waiting for hot reload lock to be released...");
61+
await Task.Delay(waitTime, token);
62+
waitTime = TimeSpan.FromSeconds(Math.Min(waitTime.TotalSeconds * 2, 120)); // Exponential backoff with a cap
63+
}
64+
catch (TaskCanceledException)
65+
{
66+
// Case: The cancellation token was triggered, meaning the lock file is still present.
67+
// We expect that something goes wrong. So, we try to delete the lock file:
68+
LOG.LogWarning("Hot reload lock file still exists after 30 seconds. Attempting to delete it...");
69+
UnlockHotReload();
70+
break;
71+
}
72+
}
73+
74+
LOG.LogInformation("Hot reload lock file released. Proceeding with plugin reload.");
75+
}
76+
5277
await LoadAll();
5378
await MessageBus.INSTANCE.SendMessage<bool>(null, Event.PLUGINS_RELOADED);
5479
}
80+
catch(Exception e)
81+
{
82+
LOG.LogError(e, $"Error while reloading plugins after change in file '{args.FullPath}' with change type '{changeType}'.");
83+
}
5584
finally
5685
{
5786
HOT_RELOAD_SEMAPHORE.Release();

app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public static partial class PluginFactory
1313
private static string PLUGINS_ROOT = string.Empty;
1414
private static string INTERNAL_PLUGINS_ROOT = string.Empty;
1515
private static string CONFIGURATION_PLUGINS_ROOT = string.Empty;
16+
private static string HOT_RELOAD_LOCK_FILE = string.Empty;
1617
private static FileSystemWatcher HOT_RELOAD_WATCHER = null!;
1718
private static ILanguagePlugin BASE_LANGUAGE_PLUGIN = NoPluginLanguage.INSTANCE;
1819

@@ -30,6 +31,7 @@ public static bool Setup()
3031
LOG.LogInformation("Initializing plugin factory...");
3132
DATA_DIR = SettingsManager.DataDirectory!;
3233
PLUGINS_ROOT = Path.Join(DATA_DIR, "plugins");
34+
HOT_RELOAD_LOCK_FILE = Path.Join(PLUGINS_ROOT, ".lock");
3335
INTERNAL_PLUGINS_ROOT = Path.Join(PLUGINS_ROOT, ".internal");
3436
CONFIGURATION_PLUGINS_ROOT = Path.Join(PLUGINS_ROOT, ".config");
3537

@@ -41,6 +43,51 @@ public static bool Setup()
4143
LOG.LogInformation("Plugin factory initialized successfully.");
4244
return true;
4345
}
46+
47+
private static async Task LockHotReloadAsync()
48+
{
49+
if (!IS_INITIALIZED)
50+
{
51+
LOG.LogError("PluginFactory is not initialized.");
52+
return;
53+
}
54+
55+
try
56+
{
57+
if (File.Exists(HOT_RELOAD_LOCK_FILE))
58+
{
59+
LOG.LogWarning("Hot reload lock file already exists.");
60+
return;
61+
}
62+
63+
await File.WriteAllTextAsync(HOT_RELOAD_LOCK_FILE, DateTime.UtcNow.ToString("o"));
64+
}
65+
catch (Exception e)
66+
{
67+
LOG.LogError(e, "An error occurred while trying to lock hot reloading.");
68+
}
69+
}
70+
71+
private static void UnlockHotReload()
72+
{
73+
if (!IS_INITIALIZED)
74+
{
75+
LOG.LogError("PluginFactory is not initialized.");
76+
return;
77+
}
78+
79+
try
80+
{
81+
if(File.Exists(HOT_RELOAD_LOCK_FILE))
82+
File.Delete(HOT_RELOAD_LOCK_FILE);
83+
else
84+
LOG.LogWarning("Hot reload lock file does not exist. Nothing to unlock.");
85+
}
86+
catch (Exception e)
87+
{
88+
LOG.LogError(e, "An error occurred while trying to unlock hot reloading.");
89+
}
90+
}
4491

4592
public static void Dispose()
4693
{
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
# v0.9.48, build 223 (2025-06-xx xx:xx UTC)
2+
- Improved German translation.
3+
- Improved how configurations are applied in organizations and made the process more robust.

documentation/Enterprise IT.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Enterprise IT
2+
3+
## Overview
4+
Do you want to manage MindWork AI Studio in a corporate environment or within an organization? This documentation explains what you need to do and how it works. First, here's an overview of the entire process:
5+
6+
- You can distribute MindWork AI Studio to employees' devices using tools like Microsoft System Center Configuration Manager (SCCM).
7+
- Employees can get updates through the built-in update feature. If you want, you can disable automatic updates and control which version gets distributed.
8+
- AI Studio checks about every 16 minutes to see where and which configuration it should load. This information is loaded from the local system. On Windows, you might use the registry, for example.
9+
- If it finds the necessary metadata, AI Studio downloads the configuration as a ZIP file from the specified server.
10+
- The configuration is an AI Studio plugin written in Lua.
11+
- Any changes to the configuration apply live while the software is running, so employees don’t need to restart it.
12+
13+
AI Studio checks about every 16 minutes to see if the configuration ID, the server for the configuration, or the configuration itself has changed. If it finds any changes, it loads the updated configuration from the server and applies it right away.
14+
15+
## Configure the devices
16+
So that MindWork AI Studio knows where to load which configuration, this information must be provided as metadata on employees’ devices. Currently, the following options are available:
17+
18+
- **Registry** (only available for Microsoft Windows): On Windows devices, AI Studio first tries to read the information from the registry. The registry information can be managed and distributed centrally as a so-called Group Policy Object (GPO).
19+
20+
- **Environment variables**: On all operating systems (on Windows as a fallback after the registry), AI Studio tries to read the configuration metadata from environment variables.
21+
22+
The following keys and values (registry) and variables are checked and read:
23+
24+
- Key `HKEY_CURRENT_USER\Software\github\MindWork AI Studio\Enterprise IT`, value `config_id` or variable `MINDWORK_AI_STUDIO_ENTERPRISE_CONFIG_ID`: This must be a valid [GUID](https://en.wikipedia.org/wiki/Universally_unique_identifier#Globally_unique_identifier). It uniquely identifies the configuration. You can use an ID per department, institute, or even per person.
25+
26+
- Key `HKEY_CURRENT_USER\Software\github\MindWork AI Studio\Enterprise IT`, value `delete_config_id` or variable `MINDWORK_AI_STUDIO_ENTERPRISE_DELETE_CONFIG_ID`: This is a configuration ID that should be removed. This is helpful if an employee moves to a different department or leaves the organization.
27+
28+
- Key `HKEY_CURRENT_USER\Software\github\MindWork AI Studio\Enterprise IT`, value `config_server_url` or variable `MINDWORK_AI_STUDIO_ENTERPRISE_CONFIG_SERVER_URL`: An HTTP or HTTPS address using an IP address or DNS name. This is the web server from which AI Studio attempts to load the specified configuration as a ZIP file.
29+
30+
Let's assume as example that `https://intranet.my-company.com:30100/ai-studio/configuration` is the server address and `9072b77d-ca81-40da-be6a-861da525ef7b` is the configuration ID. AI Studio will derive the following address from this information: `https://intranet.my-company.com:30100/ai-studio/configuration/9072b77d-ca81-40da-be6a-861da525ef7b.zip`. Important: The configuration ID will always be written in lowercase, even if it is configured in uppercase. If `9072B77D-CA81-40DA-BE6A-861DA525EF7B` is configured, the same address will be derived. Your web server must be configured accordingly.
31+
32+
Finally, AI Studio will send a GET request and download the ZIP file. The ZIP file only contains the files necessary for the configuration. It's normal to include a file for an icon along with the actual configuration plugin.
33+
34+
Approximately every 16 minutes, AI Studio checks the metadata of the ZIP file by reading the [ETag](https://en.wikipedia.org/wiki/HTTP_ETag). When the ETag was not changed, no download will be performed. Make sure that your web server supports this.
35+
36+
## Configure the configuration web server
37+
38+
In principle, you can use any web server that can serve ZIP files from a folder. However, keep in mind that AI Studio queries the file's metadata using [ETag](https://en.wikipedia.org/wiki/HTTP_ETag). Your web server must support this feature. For security reasons, you should also make sure that users cannot list the contents of the directory. This is important because the different configurations may contain confidential information such as API keys. Each user should only know their own configuration ID. Otherwise, a user might try to use someone else’s ID to gain access to exclusive resources.
39+
40+
The ZIP file names for the configurations must be in lowercase on the server, or your web server needs to ignore the spelling in requests. Also, make sure the web server is only accessible within your organization’s intranet. You don’t want the server open to everyone worldwide.
41+
42+
You can use the open source web server [Caddy](https://caddyserver.com/). The project is openly developed on [GitHub](https://github.com/caddyserver/caddy). Below you’ll find an example configuration, a so-called `Caddyfile`, for serving configurations from the folder `/localdata1/ai-studio/config` to AI Studio. The TLS certificates are loaded from the folder `/localdata1/tls-certificate`.
43+
44+
```
45+
{
46+
# Disable logging:
47+
log {
48+
output discard
49+
}
50+
51+
# Disable automatic HTTPS redirection:
52+
auto_https off
53+
}
54+
55+
intranet.my-company.com:30100 {
56+
# Load TLS certificates:
57+
tls /localdata1/tls-certificate/cert_webserver.pem /localdata1/tls-certificate/key_webserver.pem
58+
59+
# Serve the configuration files:
60+
handle_path /ai-studio/configuration/* {
61+
file_server {
62+
root /localdata1/ai-studio/config
63+
64+
# Disable directory browsing:
65+
browse false
66+
}
67+
}
68+
69+
# All other requests will receive a 404 Not Found response:
70+
handle {
71+
respond "Not Found" 404
72+
}
73+
}
74+
```
75+
76+
## Example AI Studio configuration
77+
The latest example of an AI Studio configuration via configuration plugin can always be found in the repository in the `app/MindWork AI Studio/Plugins/configuration` folder. Here are the links to the files:
78+
79+
- [The icon](../app/MindWork%20AI%20Studio/Plugins/configuration/icon.lua)
80+
- [The configuration with explanations](../app/MindWork%20AI%20Studio/Plugins/configuration/plugin.lua)
81+
82+
Please note that the icon must be an SVG vector graphic. Raster graphics like PNGs, GIFs, and others aren’t supported. You can use the sample icon, which looks like a gear.
83+
84+
Currently, you can configure the following things:
85+
- Any number of self-hosted LLM providers (a combination of server and model), but currently only without API keys
86+
- The update behavior of AI Studio
87+
88+
All other settings can be made by the user themselves. If you need additional settings, feel free to create an issue in our planning repository: https://github.com/MindWorkAI/Planning/issues
89+
90+
In the coming months, we will allow more settings, such as:
91+
- Using API keys for providers
92+
- Configuration of embedding providers for RAG
93+
- Configuration of data sources for RAG
94+
- Configuration of chat templates
95+
- Configuration of assistant plugins (for example, your own assistants for your company or specific departments)

0 commit comments

Comments
 (0)