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

Merge Requests

More

Recent comments

🇧🇪Belgium wouters_f Leuven

There is a way to skip moderations for your content:
https://www.drupal.org/project/ai/issues/3510599#comment-16028722 Allow skipping of moderations for some embeddings (controlled input) Active

🇧🇪Belgium wouters_f Leuven

Yes valid feedback added your proposed changes.
Thanks for this.

🇧🇪Belgium wouters_f Leuven

Quick local test seemed ok.

🇧🇪Belgium wouters_f Leuven

O yea that absolutely makes sense.
The spell fix issue was in lingo for a long time and other changes went in first. that explains the mismatch.
Thanks for pointing this out!

🇧🇪Belgium wouters_f Leuven

You are absolutely right and I already created a ticket for this:
https://www.drupal.org/project/ai_search_block/issues/3510772 Debounce the submit button. Active
I'll close the other one as your description is more complete.

🇧🇪Belgium wouters_f Leuven

Check also the related issue in openai provider.
https://www.drupal.org/project/ai_provider_openai/issues/3510601 allow skipping of moderations for some embeddings (not all) Active

🇧🇪Belgium wouters_f Leuven

I have seen these logs and its absolutely better than nothing.
Even better would be a dead letter queue for these messages but that would be a search_api or core queue thing.

🇧🇪Belgium wouters_f Leuven

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

🇧🇪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

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.

Production build 0.71.5 2024