Leuven
Account created on 12 February 2010, about 15 years ago
#

Merge Requests

More

Recent comments

🇧🇪Belgium wouters_f Leuven

Basic logging of queries and answers (and the nodes) is in there.

🇧🇪Belgium wouters_f Leuven

merged and fixed

🇧🇪Belgium wouters_f Leuven

wouters_f made their first commit to this issue’s fork.

🇧🇪Belgium wouters_f Leuven

MR dale smith,
I'd be curious to learn how to do that (maybe to demo for the AI workshop in davos).
Could you describe it to me a little more verbose how that should happen?
That would be awesome.

This always starts from a node right?
You can't start from a view where you select an automator and add it on top (tus for example summarise the data)?

🇧🇪Belgium wouters_f Leuven

@dotist if this is sufficient you may close this issue.

🇧🇪Belgium wouters_f Leuven

(and pulled in 1.0.x so that we dont bump into other merge conflicts)

🇧🇪Belgium wouters_f Leuven

Resolved the comments that made sense, the hyperlink one I think should not happen.

🇧🇪Belgium wouters_f Leuven

I've added an example below.
You can also find working code
- here (backend)
- and here (javascript)

The (simplified a bit) example:
Backend:

      $output = $provider->chat($input, $ai_model_to_use, ['my_module_tag']);
      $response = $output->getNormalized();
return new StreamedResponse(function () use ($response) {
      $log_output = '';
      foreach ($responseas $part) {
        $item = [];
        $item['answer_piece'] = $part->getText();
        $out = Json::encode($item);
        echo $out . '|§|';
        ob_flush();
        flush();
      }
    }, 200, [
      'Cache-Control' => 'no-cache, must-revalidate',
      'Content-Type' => 'text/event-stream',
      'X-Accel-Buffering' => 'no',
    ]);

And in the frontend you want to catch it like so:

try {
              var xhr = new XMLHttpRequest();
              xhr.open('POST', drupalSettings.ai_search_block.submit_url, true);
              xhr.setRequestHeader('Content-Type', 'application/json');
              xhr.setRequestHeader('Accept', 'application/json');

              // Cache variables to hold the full output.
              var lastResponseLength = 0;
              var joined = '';

              xhr.onprogress = function () {
                var responseText = xhr.responseText || '';
                // Get only the new part of the response.
                var newData = responseText.substring(lastResponseLength);
                lastResponseLength = responseText.length;

                // Split new data using the delimiter.
                var chunks = newData.trim().split('|§|').filter(Boolean);

                // Parse each chunk and accumulate the answer pieces.
                chunks.forEach(function (chunk) {
                  try {
                    var parsed = JSON.parse(chunk);
                    joined += parsed.answer_piece || '';
                  } catch (e) {
                    console.error('Error parsing chunk:', e, chunk);
                  }
                });

                // Overwrite the full output (letting browsers fix broken HTML)
                // and re-append the loader.
                $resultsBlock.html(joined).append($loader);
              };

              xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                  if (xhr.status === 200) {
                    // Remove the loader upon successful completion.
                    $loader.remove();
                    if ($suffixText.length) {
                      $suffixText.html(drupalSettings.ai_search_block.suffix_text);
                      Drupal.attachBehaviors($suffixText[0]);
                      $suffixText.show();
                    }
                    // (Optional) If needed, update log Id from final response here.
                  } else if (xhr.status === 500) {
                    $resultsBlock.html('An error happened.');
                    console.error('Error response:', xhr.responseText);
                    try {
                      var parsedError = JSON.parse(xhr.responseText);
                      if (parsedError.response && parsedError.response.answer_piece) {
                        $resultsBlock.html(parsedError.response.answer_piece);
                      }
                      Drupal.attachBehaviors($resultsBlock[0]);
                    } catch (e) {
                      console.error('Error parsing 500 response:', e);
                    }
                  }
                }
              };

              // Send the streaming request.
              xhr.send(
                  JSON.stringify({
                    query: queryVal,
                    stream: streamVal,
                    block_id: blockIdVal
                  })
              );
            } catch (e) {
              console.error('XHR error:', e);
            }
🇧🇪Belgium wouters_f Leuven

I thin kif you use the openai module you should be ale to switch the backend calls pretty seamlessly.
I can imagine you don't want to rebuild the AI keys and connectors in the frontend so having them as an API seems obvious to me.
I'm not aware of plans of exposing them as generic API's at the moment.
there are some (specific) implementations that are not bound to a vendor and work with all ai providers.

- Check the controller for translations in ai_translate,
- check the controller for completion in ai_ckeditor
- there's a controller for image generation in ai_image

Having them as a separate module could make sense.
I'd call it ai_api, which you can then use in your frontends.
If someone built that I would consider switching those modules to use the generic one.

You could ask Marcus What the plans or ideas are since he's in the lead at the moment.

🇧🇪Belgium wouters_f Leuven

These two are boilerplate I dont even remember from where. They can be removed.

🇧🇪Belgium wouters_f Leuven

Manually tested. Good to go.

🇧🇪Belgium wouters_f Leuven

wouters_f made their first commit to this issue’s fork.

🇧🇪Belgium wouters_f Leuven

Strange that this sneeked in,

🇧🇪Belgium wouters_f Leuven

This fix looks pretty straightforward to me. Good for me.

🇧🇪Belgium wouters_f Leuven

Tested and merged!

🇧🇪Belgium wouters_f Leuven

I think after that's fixed, it looks good.

🇧🇪Belgium wouters_f Leuven

I tested it manually and:
if i disable streaming it works.
If i enable streaming, the server streams back the responses, but the js seems not to do anything anymore.

I've debugged it a bit and see that you now test "streamVal" which used to be
const $stream = $form.find('[data-drupal-selector="edit-stream"]').val() === 'true';
but you now test it with
if (streamVal === '1') {
So I guess that's why it stopped working.

🇧🇪Belgium wouters_f Leuven

Hey Sirclickalot, great idea.
If you're interested I'm willing to set something up.
maybe there's already a solution for you with automators or with ECA and the ECA Ai plugins .
With ECA you could use the event submission, and then trigger a AI node with your fancy prompt (and the assignment as input).
The output can then be put in a separate field (or wherever).

But obviously we can also make a separate module with a ckeditor plugin that triggers a prompt. so many options :D

🇧🇪Belgium wouters_f Leuven

if you are really blocked, you can try to create a ai_content folder in ai/modules and add a ai_content.info.yml with some contents

name: AI Content
description: Placeholder.
package: AI
type: module
core_version_requirement: ^10.2 || ^11
dependencies:
  - ai:ai

That will allow you to uninstall it and fter that you can remove it again.

🇧🇪Belgium wouters_f Leuven

What happened is the following.
At some point the submodule ai_content was renamed to ai_content_suggestions.
I propose to clear caches, uninstall that module (if enabled) clear caches again and then install the ai_content_suggestions submodule.

🇧🇪Belgium wouters_f Leuven

So two changes in this one:

  1. All fields selected by default
  2. Added some Javascript for cleaner interface (hide the selects and toggle on click)

Goal: editors have an easier, less cluttered experience using AI.

🇧🇪Belgium wouters_f Leuven

added an implementation with JS.
(all keeps working without JS).

🇧🇪Belgium wouters_f Leuven

I did not test it but the code looks sound.
I'm not sure, should we also check $account->hasPermission('translate any entity') OR will that happen automatically in $handler->getTranslationAccess($entity, 'create')->isAllowed()?

🇧🇪Belgium wouters_f Leuven

Fixed coding standards and used the renamed branch (1.0.x was a strange branch name there).

🇧🇪Belgium wouters_f Leuven

If we use provider modules and we want them to support the proxy, we'll indeed need to add a proxy to the providers if they are using something else.

🇧🇪Belgium wouters_f Leuven

Any outhgoing request using \Drupal::httpClient() wil normally take the proxy settings into account.
So if we are using guzzle directly and not the drupal client your issue might indeed be true.

We should use the drupal wrapper for every outhgoing request in orde to have everything working over a proxy.

🇧🇪Belgium wouters_f Leuven

Removed the hardcoded colors, and now:

🇧🇪Belgium wouters_f Leuven

wouters_f made their first commit to this issue’s fork.

🇧🇪Belgium wouters_f Leuven

We wont be fixing this here then.
@lammensj I trust you'll make a ticket for deprecating the internal eca module in favour of the spinoff separate one?

🇧🇪Belgium wouters_f Leuven

(there is also a merge conflict which might prevent it from getting merged. might be best to resolve that too).

🇧🇪Belgium wouters_f Leuven

Tested this:
Gave me this error:
NOTICE: PHP message: Uncaught PHP Exception AssertionError: "Failed to assert that "ai_content_suggestions_plugins, ai_providers, entity_types, user.roles:authenticated" are valid cache contexts." at /var/www/html/web/core/lib/Drupal/Core/Cache/Cache.php line 31
If i removed the line

$form['#cache']['contexts'][] = 'entity_types';

From the Settings form, it worked as expected.

🇧🇪Belgium wouters_f Leuven

wouters_f made their first commit to this issue’s fork.

🇧🇪Belgium wouters_f Leuven

Manually tested. Looks good to me.

🇧🇪Belgium wouters_f Leuven

tested with japanese stable diffusion, dalle2,3 and normal stable diffusion

🇧🇪Belgium wouters_f Leuven

wouters_f made their first commit to this issue’s fork.

🇧🇪Belgium wouters_f Leuven

It works in the ai module and I think most people will (and even should) switch to that one.
Anup can help with the maintenance there, then we dont need to maintain duplicate modules (and the AI module has more functionalities).
For me this may be closed (outdated).

🇧🇪Belgium wouters_f Leuven

It has a stable 1.0.3 release now. This should work for you. If you need any help let us know in the #ai channel. many people there willing to help.

🇧🇪Belgium wouters_f Leuven

Since its merged i think we can close this no?

🇧🇪Belgium wouters_f Leuven

Very nice!
I'm already advocating for it/

🇧🇪Belgium wouters_f Leuven

Yes I think so too.

🇧🇪Belgium wouters_f Leuven

Most of these were implemented.

🇧🇪Belgium wouters_f Leuven

Ok so yes ok I tried it out.
Once I got the hang of it !
Really cool. working perfect (as usual).

I also agree with the form complexity, it's a bit overwhelming indeed. On the other hand we give editors maximal power and I got it once I started reading the description texts.

I was searching for 10 minutes first because I was used to (i've always done it like that, not saying its everybody) configuring metatags at admin/config/search/metatag and there's no mention of ai_automators there whatsoever. So i was confused.
It's also there you have the inheritance system for metatags, so I guess maybe that's why i was expecting it there?

Apart from that: Very nice, thanks!

🇧🇪Belgium wouters_f Leuven

I cant replicate this anymore @fishfree, Is this still relevant?

🇧🇪Belgium wouters_f Leuven

Processed most of these.

  1. split on a little omre complex character now (not sure if more complexity is needed)
  2. Json decode replaced
  3. POST vars replaced
  4. problems with 2 instances: that is correct. it's either locked to 1 block without conflicts (had this in the first version) or over two block with possible conflicts. Currently this is how I want to do it. might change later
  5. worth making the results into a template: will make a separate issue for that
  6. max age 0 and cache dissabling removed (was for development)
  7. layout builder references removed, we are not dependant on layout builder at all
  8. added a select box for the permission check. I hear others want other permission systems too, so we'll have the choice to change later
  9. Will make a separate issue for views access checking of filtering
  10. Converting back to HTML: also implementing this,
🇧🇪Belgium wouters_f Leuven

Changes the text on the AI module page and added in your copy here.
This should be sufficient like this.

🇧🇪Belgium wouters_f Leuven

Updated the schema yml to represent the install settings.
This better?

🇧🇪Belgium wouters_f Leuven

I've committed this text:
You have no AI providers enabled. Please install and enable a provider module.
I'm proposing a merge request like this.

🇧🇪Belgium wouters_f Leuven

What do you think if We show a warning on /admin/config/ai/settings ?
Something like this?

🇧🇪Belgium wouters_f Leuven

Could it be something like this that is needed?

/**
 * Implements hook_modules_installed().
 */
function ai_modules_installed(array $modules) {
  // Clear the provider cache when a module is installed.
  \Drupal::cache('discovery')->delete('ai_provider_plugins');
}
Production build 0.71.5 2024