diff --git a/app/MindWork AI Studio/Assistants/JobPosting/AssistantJobPostings.razor b/app/MindWork AI Studio/Assistants/JobPosting/AssistantJobPostings.razor
new file mode 100644
index 0000000..ba41922
--- /dev/null
+++ b/app/MindWork AI Studio/Assistants/JobPosting/AssistantJobPostings.razor
@@ -0,0 +1,15 @@
+@attribute [Route(Routes.ASSISTANT_JOB_POSTING)]
+@inherits AssistantBaseCore
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Assistants/JobPosting/AssistantJobPostings.razor.cs b/app/MindWork AI Studio/Assistants/JobPosting/AssistantJobPostings.razor.cs
new file mode 100644
index 0000000..8da1250
--- /dev/null
+++ b/app/MindWork AI Studio/Assistants/JobPosting/AssistantJobPostings.razor.cs
@@ -0,0 +1,283 @@
+namespace AIStudio.Assistants.JobPosting;
+
+public partial class AssistantJobPostings : AssistantBaseCore
+{
+ public override Tools.Components Component => Tools.Components.JOB_POSTING_ASSISTANT;
+
+ protected override string Title => "Job Posting";
+
+ protected override string Description =>
+ """
+ Provide some key points about the job you want to post. The AI will then
+ formulate a suggestion that you can finalize.
+ """;
+
+ protected override string SystemPrompt =>
+ $"""
+ You are an experienced specialist in personnel matters. You write job postings.
+ You follow the usual guidelines in the country of the posting. You ensure that
+ no individuals are discriminated against or excluded by the job posting. You do
+ not ask any questions and you do not repeat the task. You provide your response
+ in a Markdown format. Missing information necessary for the job posting, you try
+ to derive from the other details provided.
+
+ Structure of your response:
+ ---
+ #
+
+
+ #
+
+
+ #
+
+
+ #
+
+
+
+
+ ---
+
+ You write the job posting in the following language: {this.SystemPromptLanguage()}.
+ """;
+
+ protected override IReadOnlyList FooterButtons => [];
+
+ protected override string SubmitText => "Create the job posting";
+
+ protected override Func SubmitAction => this.CreateJobPosting;
+
+ protected override bool SubmitDisabled => false;
+
+ protected override bool AllowProfiles => false;
+
+ protected override void ResetFrom()
+ {
+ this.inputEntryDate = string.Empty;
+ this.inputValidUntil = string.Empty;
+ if (!this.MightPreselectValues())
+ {
+ this.selectedTargetLanguage = CommonLanguages.AS_IS;
+ this.customTargetLanguage = string.Empty;
+ this.inputMandatoryInformation = string.Empty;
+ this.inputJobDescription = string.Empty;
+ this.inputQualifications = string.Empty;
+ this.inputResponsibilities = string.Empty;
+ this.inputCompanyName = string.Empty;
+ this.inputWorkLocation = string.Empty;
+ this.inputCountryLegalFramework = string.Empty;
+ }
+ }
+
+ protected override bool MightPreselectValues()
+ {
+ if (this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)
+ {
+ this.inputMandatoryInformation = this.SettingsManager.ConfigurationData.JobPostings.PreselectedMandatoryInformation;
+ if(string.IsNullOrWhiteSpace(this.inputJobDescription))
+ this.inputJobDescription = this.SettingsManager.ConfigurationData.JobPostings.PreselectedJobDescription;
+ this.inputQualifications = this.SettingsManager.ConfigurationData.JobPostings.PreselectedQualifications;
+ this.inputResponsibilities = this.SettingsManager.ConfigurationData.JobPostings.PreselectedResponsibilities;
+ this.inputCompanyName = this.SettingsManager.ConfigurationData.JobPostings.PreselectedCompanyName;
+ this.inputWorkLocation = this.SettingsManager.ConfigurationData.JobPostings.PreselectedWorkLocation;
+ this.inputCountryLegalFramework = this.SettingsManager.ConfigurationData.JobPostings.PreselectedCountryLegalFramework;
+ this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.JobPostings.PreselectedTargetLanguage;
+ this.customTargetLanguage = this.SettingsManager.ConfigurationData.JobPostings.PreselectOtherLanguage;
+ return true;
+ }
+
+ return false;
+ }
+
+ private string inputMandatoryInformation = string.Empty;
+ private string inputJobDescription = string.Empty;
+ private string inputQualifications = string.Empty;
+ private string inputResponsibilities = string.Empty;
+ private string inputCompanyName = string.Empty;
+ private string inputEntryDate = string.Empty;
+ private string inputValidUntil = string.Empty;
+ private string inputWorkLocation = string.Empty;
+ private string inputCountryLegalFramework = string.Empty;
+ private CommonLanguages selectedTargetLanguage = CommonLanguages.AS_IS;
+ private string customTargetLanguage = string.Empty;
+
+ #region Overrides of ComponentBase
+
+ protected override async Task OnInitializedAsync()
+ {
+ var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages(Event.SEND_TO_JOB_POSTING_ASSISTANT).FirstOrDefault();
+ if (deferredContent is not null)
+ this.inputJobDescription = deferredContent;
+
+ await base.OnInitializedAsync();
+ }
+
+ #endregion
+
+ private string? ValidateCustomLanguage(string language)
+ {
+ if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
+ return "Please provide a custom target language.";
+
+ return null;
+ }
+
+ private string? ValidateJobDescription(string jobDescription)
+ {
+ if(string.IsNullOrWhiteSpace(jobDescription))
+ return "Please provide a job description.";
+
+ return null;
+ }
+
+ private string? ValidateCountryLegalFramework(string countryLegalFramework)
+ {
+ if(string.IsNullOrWhiteSpace(countryLegalFramework))
+ return "Please provide the country where the job is posted (legal framework).";
+
+ return null;
+ }
+
+ private string SystemPromptLanguage()
+ {
+ if(this.selectedTargetLanguage is CommonLanguages.AS_IS)
+ return "Use the same language as the input";
+
+ if(this.selectedTargetLanguage is CommonLanguages.OTHER)
+ return this.customTargetLanguage;
+
+ return this.selectedTargetLanguage.Name();
+ }
+
+ private string UserPromptMandatoryInformation()
+ {
+ if(string.IsNullOrWhiteSpace(this.inputMandatoryInformation))
+ return string.Empty;
+
+ return $"""
+ # Mandatory Information
+ {this.inputMandatoryInformation}
+
+ """;
+ }
+
+ private string UserPromptJobDescription()
+ {
+ if(string.IsNullOrWhiteSpace(this.inputJobDescription))
+ return string.Empty;
+
+ return $"""
+ # Job Description
+ {this.inputJobDescription}
+
+ """;
+ }
+
+ private string UserPromptQualifications()
+ {
+ if(string.IsNullOrWhiteSpace(this.inputQualifications))
+ return string.Empty;
+
+ return $"""
+ # Qualifications
+ {this.inputQualifications}
+
+ """;
+ }
+
+ private string UserPromptResponsibilities()
+ {
+ if(string.IsNullOrWhiteSpace(this.inputResponsibilities))
+ return string.Empty;
+
+ return $"""
+ # Responsibilities
+ {this.inputResponsibilities}
+
+ """;
+ }
+
+ private string UserPromptCompanyName()
+ {
+ if(string.IsNullOrWhiteSpace(this.inputCompanyName))
+ return string.Empty;
+
+ return $"""
+ # Company Name
+ {this.inputCompanyName}
+
+ """;
+ }
+
+ private string UserPromptEntryDate()
+ {
+ if(string.IsNullOrWhiteSpace(this.inputEntryDate))
+ return string.Empty;
+
+ return $"""
+ # Entry Date
+ {this.inputEntryDate}
+
+ """;
+ }
+
+ private string UserPromptValidUntil()
+ {
+ if(string.IsNullOrWhiteSpace(this.inputValidUntil))
+ return string.Empty;
+
+ return $"""
+ # Job Posting Valid Until
+ {this.inputValidUntil}
+
+ """;
+ }
+
+ private string UserPromptWorkLocation()
+ {
+ if(string.IsNullOrWhiteSpace(this.inputWorkLocation))
+ return string.Empty;
+
+ return $"""
+ # Work Location
+ {this.inputWorkLocation}
+
+ """;
+ }
+
+ private string UserPromptCountryLegalFramework()
+ {
+ if(string.IsNullOrWhiteSpace(this.inputCountryLegalFramework))
+ return string.Empty;
+
+ return $"""
+ # Country where the job is posted (legal framework)
+ {this.inputCountryLegalFramework}
+
+ """;
+ }
+
+ private async Task CreateJobPosting()
+ {
+ await this.form!.Validate();
+ if (!this.inputIsValid)
+ return;
+
+ this.CreateChatThread();
+ var time = this.AddUserRequest(
+ $"""
+ {this.UserPromptCompanyName()}
+ {this.UserPromptCountryLegalFramework()}
+ {this.UserPromptMandatoryInformation()}
+ {this.UserPromptJobDescription()}
+ {this.UserPromptQualifications()}
+ {this.UserPromptResponsibilities()}
+ {this.UserPromptWorkLocation()}
+ {this.UserPromptEntryDate()}
+ {this.UserPromptValidUntil()}
+ """);
+
+ await this.AddAIResponseAsync(time);
+ }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Components/ConfigurationText.razor b/app/MindWork AI Studio/Components/ConfigurationText.razor
index 73d07be..a3cc323 100644
--- a/app/MindWork AI Studio/Components/ConfigurationText.razor
+++ b/app/MindWork AI Studio/Components/ConfigurationText.razor
@@ -3,16 +3,17 @@
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Components/ConfigurationText.razor.cs b/app/MindWork AI Studio/Components/ConfigurationText.razor.cs
index e6cd379..4e6bb7f 100644
--- a/app/MindWork AI Studio/Components/ConfigurationText.razor.cs
+++ b/app/MindWork AI Studio/Components/ConfigurationText.razor.cs
@@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Components;
+using Timer = System.Timers.Timer;
+
namespace AIStudio.Components;
public partial class ConfigurationText : ConfigurationBase
@@ -11,7 +13,7 @@ public partial class ConfigurationText : ConfigurationBase
public Func Text { get; set; } = () => string.Empty;
///
- /// An action which is called when the option is changed.
+ /// An action which is called when the text was changed.
///
[Parameter]
public Action TextUpdate { get; set; } = _ => { };
@@ -27,6 +29,55 @@ public partial class ConfigurationText : ConfigurationBase
///
[Parameter]
public Color IconColor { get; set; } = Color.Default;
+
+ ///
+ /// How many lines should the textfield have?
+ ///
+ [Parameter]
+ public int NumLines { get; set; } = 1;
+
+ ///
+ /// What is the maximum number of lines?
+ ///
+ [Parameter]
+ public int MaxLines { get; set; } = 12;
+
+ private string internalText = string.Empty;
+ private Timer timer = new(TimeSpan.FromMilliseconds(500))
+ {
+ AutoReset = false
+ };
+
+ #region Overrides of ConfigurationBase
+
+ protected override async Task OnInitializedAsync()
+ {
+ this.timer.Elapsed += async (_, _) => await this.InvokeAsync(async () => await this.OptionChanged(this.internalText));
+ await base.OnInitializedAsync();
+ }
+
+ #region Overrides of ComponentBase
+
+ protected override async Task OnParametersSetAsync()
+ {
+ this.internalText = this.Text();
+ await base.OnParametersSetAsync();
+ }
+
+ #endregion
+
+ #endregion
+
+ private bool AutoGrow => this.NumLines > 1;
+
+ private int GetMaxLines => this.AutoGrow ? this.MaxLines : 1;
+
+ private void InternalUpdate(string text)
+ {
+ this.timer.Stop();
+ this.internalText = text;
+ this.timer.Start();
+ }
private async Task OptionChanged(string updatedText)
{
diff --git a/app/MindWork AI Studio/Pages/Assistants.razor b/app/MindWork AI Studio/Pages/Assistants.razor
index da377d7..2368878 100644
--- a/app/MindWork AI Studio/Pages/Assistants.razor
+++ b/app/MindWork AI Studio/Pages/Assistants.razor
@@ -24,6 +24,7 @@
+
diff --git a/app/MindWork AI Studio/Pages/Settings.razor b/app/MindWork AI Studio/Pages/Settings.razor
index 27e565e..df5ed94 100644
--- a/app/MindWork AI Studio/Pages/Settings.razor
+++ b/app/MindWork AI Studio/Pages/Settings.razor
@@ -342,6 +342,26 @@
+
+
+
+
+
+
+
+
+
+
+
+ @if (this.SettingsManager.ConfigurationData.JobPostings.PreselectedTargetLanguage is CommonLanguages.OTHER)
+ {
+
+ }
+
+
+
+
+
diff --git a/app/MindWork AI Studio/Provider/ProvidersExtensions.cs b/app/MindWork AI Studio/Provider/LLMProvidersExtensions.cs
similarity index 98%
rename from app/MindWork AI Studio/Provider/ProvidersExtensions.cs
rename to app/MindWork AI Studio/Provider/LLMProvidersExtensions.cs
index 8a4a971..3037e87 100644
--- a/app/MindWork AI Studio/Provider/ProvidersExtensions.cs
+++ b/app/MindWork AI Studio/Provider/LLMProvidersExtensions.cs
@@ -7,7 +7,7 @@ using AIStudio.Settings;
namespace AIStudio.Provider;
-public static class ProvidersExtensions
+public static class LLMProvidersExtensions
{
///
/// Returns the human-readable name of the provider.
diff --git a/app/MindWork AI Studio/Routes.razor.cs b/app/MindWork AI Studio/Routes.razor.cs
index f65d7ec..7b3e24e 100644
--- a/app/MindWork AI Studio/Routes.razor.cs
+++ b/app/MindWork AI Studio/Routes.razor.cs
@@ -21,5 +21,6 @@ public sealed partial class Routes
public const string ASSISTANT_LEGAL_CHECK = "/assistant/legal-check";
public const string ASSISTANT_SYNONYMS = "/assistant/synonyms";
public const string ASSISTANT_MY_TASKS = "/assistant/my-tasks";
+ public const string ASSISTANT_JOB_POSTING = "/assistant/job-posting";
// ReSharper restore InconsistentNaming
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Settings/DataModel/Data.cs b/app/MindWork AI Studio/Settings/DataModel/Data.cs
index 5970f6d..720d0e0 100644
--- a/app/MindWork AI Studio/Settings/DataModel/Data.cs
+++ b/app/MindWork AI Studio/Settings/DataModel/Data.cs
@@ -58,11 +58,13 @@ public sealed class Data
public DataRewriteImprove RewriteImprove { get; init; } = new();
- public DataEMail EMail { get; set; } = new();
+ public DataEMail EMail { get; init; } = new();
- public DataLegalCheck LegalCheck { get; set; } = new();
+ public DataLegalCheck LegalCheck { get; init; } = new();
- public DataSynonyms Synonyms { get; set; } = new();
+ public DataSynonyms Synonyms { get; init; } = new();
- public DataMyTasks MyTasks { get; set; } = new();
+ public DataMyTasks MyTasks { get; init; } = new();
+
+ public DataJobPostings JobPostings { get; init; } = new();
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Settings/DataModel/DataJobPostings.cs b/app/MindWork AI Studio/Settings/DataModel/DataJobPostings.cs
new file mode 100644
index 0000000..add799e
--- /dev/null
+++ b/app/MindWork AI Studio/Settings/DataModel/DataJobPostings.cs
@@ -0,0 +1,66 @@
+using AIStudio.Provider;
+
+namespace AIStudio.Settings.DataModel;
+
+public sealed class DataJobPostings
+{
+ ///
+ /// Do we want to preselect any translator options?
+ ///
+ public bool PreselectOptions { get; set; }
+
+ ///
+ /// The mandatory information for the job posting.
+ ///
+ public string PreselectedMandatoryInformation { get; set; } = string.Empty;
+
+ ///
+ /// The job description.
+ ///
+ public string PreselectedJobDescription { get; set; } = string.Empty;
+
+ ///
+ /// The qualifications required for the job.
+ ///
+ public string PreselectedQualifications { get; set; } = string.Empty;
+
+ ///
+ /// What are the responsibilities of the job?
+ ///
+ public string PreselectedResponsibilities { get; set; } = string.Empty;
+
+ ///
+ /// Which company name should be preselected?
+ ///
+ public string PreselectedCompanyName { get; set; } = string.Empty;
+
+ ///
+ /// Where should the work be done?
+ ///
+ public string PreselectedWorkLocation { get; set; } = string.Empty;
+
+ ///
+ /// The preselected country legal framework.
+ ///
+ public string PreselectedCountryLegalFramework { get; set; } = string.Empty;
+
+ ///
+ /// Preselect the target language?
+ ///
+ public CommonLanguages PreselectedTargetLanguage { get; set; } = CommonLanguages.EN_US;
+
+ ///
+ /// Preselect any other language?
+ ///
+ public string PreselectOtherLanguage { get; set; } = string.Empty;
+
+ ///
+ /// The minimum confidence level required for a provider to be considered.
+ ///
+ public ConfidenceLevel MinimumProviderConfidence { get; set; } = ConfidenceLevel.NONE;
+
+ ///
+ /// The preselected translator provider.
+ ///
+ public string PreselectedProvider { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Tools/Components.cs b/app/MindWork AI Studio/Tools/Components.cs
index 1076585..ccebcd0 100644
--- a/app/MindWork AI Studio/Tools/Components.cs
+++ b/app/MindWork AI Studio/Tools/Components.cs
@@ -15,6 +15,7 @@ public enum Components
LEGAL_CHECK_ASSISTANT,
SYNONYMS_ASSISTANT,
MY_TASKS_ASSISTANT,
+ JOB_POSTING_ASSISTANT,
CHAT,
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Tools/ComponentsExtensions.cs b/app/MindWork AI Studio/Tools/ComponentsExtensions.cs
index e2b4628..f9f9e8e 100644
--- a/app/MindWork AI Studio/Tools/ComponentsExtensions.cs
+++ b/app/MindWork AI Studio/Tools/ComponentsExtensions.cs
@@ -18,6 +18,7 @@ public static class ComponentsExtensions
Components.LEGAL_CHECK_ASSISTANT => "Legal Check Assistant",
Components.SYNONYMS_ASSISTANT => "Synonym Assistant",
Components.MY_TASKS_ASSISTANT => "My Tasks Assistant",
+ Components.JOB_POSTING_ASSISTANT => "Job Posting Assistant",
Components.CHAT => "New Chat",
@@ -37,6 +38,7 @@ public static class ComponentsExtensions
Components.LEGAL_CHECK_ASSISTANT => new(Event.SEND_TO_LEGAL_CHECK_ASSISTANT, Routes.ASSISTANT_LEGAL_CHECK),
Components.SYNONYMS_ASSISTANT => new(Event.SEND_TO_SYNONYMS_ASSISTANT, Routes.ASSISTANT_SYNONYMS),
Components.MY_TASKS_ASSISTANT => new(Event.SEND_TO_MY_TASKS_ASSISTANT, Routes.ASSISTANT_MY_TASKS),
+ Components.JOB_POSTING_ASSISTANT => new(Event.SEND_TO_JOB_POSTING_ASSISTANT, Routes.ASSISTANT_JOB_POSTING),
Components.CHAT => new(Event.SEND_TO_CHAT, Routes.CHAT),
@@ -56,6 +58,7 @@ public static class ComponentsExtensions
Components.LEGAL_CHECK_ASSISTANT => settingsManager.ConfigurationData.LegalCheck.PreselectOptions ? settingsManager.ConfigurationData.LegalCheck.MinimumProviderConfidence : default,
Components.SYNONYMS_ASSISTANT => settingsManager.ConfigurationData.Synonyms.PreselectOptions ? settingsManager.ConfigurationData.Synonyms.MinimumProviderConfidence : default,
Components.MY_TASKS_ASSISTANT => settingsManager.ConfigurationData.MyTasks.PreselectOptions ? settingsManager.ConfigurationData.MyTasks.MinimumProviderConfidence : default,
+ Components.JOB_POSTING_ASSISTANT => settingsManager.ConfigurationData.JobPostings.PreselectOptions ? settingsManager.ConfigurationData.JobPostings.MinimumProviderConfidence : default,
_ => default,
};
@@ -73,6 +76,7 @@ public static class ComponentsExtensions
Components.LEGAL_CHECK_ASSISTANT => settingsManager.ConfigurationData.LegalCheck.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.LegalCheck.PreselectedProvider) : default,
Components.SYNONYMS_ASSISTANT => settingsManager.ConfigurationData.Synonyms.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Synonyms.PreselectedProvider) : default,
Components.MY_TASKS_ASSISTANT => settingsManager.ConfigurationData.MyTasks.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.MyTasks.PreselectedProvider) : default,
+ Components.JOB_POSTING_ASSISTANT => settingsManager.ConfigurationData.JobPostings.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.JobPostings.PreselectedProvider) : default,
Components.CHAT => settingsManager.ConfigurationData.Chat.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Chat.PreselectedProvider) : default,
diff --git a/app/MindWork AI Studio/Tools/Event.cs b/app/MindWork AI Studio/Tools/Event.cs
index 54ae9d0..691f8b3 100644
--- a/app/MindWork AI Studio/Tools/Event.cs
+++ b/app/MindWork AI Studio/Tools/Event.cs
@@ -30,4 +30,5 @@ public enum Event
SEND_TO_LEGAL_CHECK_ASSISTANT,
SEND_TO_SYNONYMS_ASSISTANT,
SEND_TO_MY_TASKS_ASSISTANT,
+ SEND_TO_JOB_POSTING_ASSISTANT,
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/wwwroot/changelog/v0.9.12.md b/app/MindWork AI Studio/wwwroot/changelog/v0.9.12.md
new file mode 100644
index 0000000..7d252de
--- /dev/null
+++ b/app/MindWork AI Studio/wwwroot/changelog/v0.9.12.md
@@ -0,0 +1,4 @@
+# v0.9.12, build 187 (2024-09-xx xx:xx UTC)
+- Added a job posting assistant to the business category.
+- Fixed margin-related issue in the `ConfigurationText` component.
+- Refactored the `ConfigurationText` component to debounce the input field to prevent unnecessary configuration updates. The component now also supports multiline text.
\ No newline at end of file